    001/*****************************************************************************
    002 * Copyright by The HDF Group.                                               *
    003 * Copyright by the Board of Trustees of the University of Illinois.         *
    004 * All rights reserved.                                                      *
    005 *                                                                           *
    006 * This file is part of the HDF Java Products distribution.                  *
    007 * The full copyright notice, including terms governing use, modification,   *
    008 * and redistribution, is contained in the COPYING file, which can be found  *
    009 * at the root of the source code distribution tree,                         *
    010 * or in https://www.hdfgroup.org/licenses.                                  *
    011 * If you do not have access to either file, you may request a copy from     *
    012 * help@hdfgroup.org.                                                        *
    013 ****************************************************************************/
    014
    015package hdf.object;
    016
    017import java.util.Iterator;
    018import java.util.LinkedList;
    019import java.util.List;
    020import java.util.Queue;
    021import java.util.Stack;
    022import java.util.Vector;
    023
    024/**
    025 * Group is an abstract class. Current implementing classes are the H4Group and
    026 * H5Group. This class includes general information of a group object such as
    027 * members of a group and common operations on groups.
    028 *
    029 * Members of a group may include other groups, datasets or links.
    030 *
    031 * @version 1.1 9/4/2007
    032 * @author Peter X. Cao
    033 */
    034public abstract class Group extends HObject implements MetaDataContainer {
    035
    036    private static final long serialVersionUID = 3913174542591568052L;
    037
    038    /**
    039     * The list of members (Groups and Datasets) of this group in memory.
    040     */
    041    private List<HObject> memberList;
    042
    043    /**
    044     * The parent group where this group is located. The parent of the root
    045     * group is null.
    046     */
    047    protected Group parent;
    048
    049    /**
    050     * Total number of members of this group in file.
    051     */
    052    protected int nMembersInFile;
    053
    054    /** The value of LINK_TYPE_HARD */
    055    public static final int LINK_TYPE_HARD = 0;
    056
    057    /** The value of LINK_TYPE_SOFT */
    058    public static final int LINK_TYPE_SOFT = 1;
    059
    060    /** The value of LINK_TYPE_EXTERNAL */
    061    public static final int LINK_TYPE_EXTERNAL = 64;
    062
    063    /** The value of CRT_ORDER_TRACKED */
    064    public static final int CRT_ORDER_TRACKED = 1;
    065
    066    /** The value of CRT_ORDER_INDEXED */
    067    public static final int CRT_ORDER_INDEXED = 2;
    068
    069
    070    /**
    071     * Constructs an instance of the group with specific name, path and parent
    072     * group. An HDF data object must have a name. The path is the group path
    073     * starting from the root. The parent group is the group where this group is
    074     * located.
    075     *
    076     * For example, in H5Group(h5file, "grp", "/groups/", pgroup), "grp" is the
    077     * name of the group, "/groups/" is the group path of the group, and pgroup
    078     * is the group where "grp" is located.
    079     *
    080     * @param theFile
    081     *            the file containing the group.
    082     * @param grpName
    083     *            the name of this group, e.g. "grp01".
    084     * @param grpPath
    085     *            the full path of this group, e.g. "/groups/".
    086     * @param grpParent
    087     *            the parent of this group.
    088     */
    089    public Group(FileFormat theFile, String grpName, String grpPath, Group grpParent) {
    090        this(theFile, grpName, grpPath, grpParent, null);
    091    }
    092
    093    /**
    094     * @deprecated Not for public use in the future.<br>
    095     *             Using {@link #Group(FileFormat, String, String, Group)}
    096     *
    097     * @param theFile
    098     *            the file containing the group.
    099     * @param grpName
    100     *            the name of this group, e.g. "grp01".
    101     * @param grpPath
    102     *            the full path of this group, e.g. "/groups/".
    103     * @param grpParent
    104     *            the parent of this group.
    105     * @param oid
    106     *            the oid of this group.
    107     */
    108    @Deprecated
    109    public Group(FileFormat theFile, String grpName, String grpPath, Group grpParent, long[] oid) {
    110        super(theFile, grpName, grpPath, oid);
    111
    112        this.parent = grpParent;
    113    }
    114
    115    /**
    116     * Clears up member list and other resources in memory for the group. Since
    117     * the destructor will clear memory space, the function is usually not
    118     * needed.
    119     */
    120    public void clear() {
    121        if (memberList != null)
    122            ((Vector<HObject>) memberList).setSize(0);
    123    }
    124
    125    /**
    126     * Adds an object to the member list of this group in memory.
    127     *
    128     * @param object
    129     *            the HObject to be added to the member list.
    130     */
    131    public void addToMemberList(HObject object) {
    132        if (memberList == null) {
    133            int size = Math.min(getNumberOfMembersInFile(), this.getFileFormat().getMaxMembers());
    134            memberList = new Vector<>(size + 5);
    135        }
    136
    137        if ((object != null) && !memberList.contains(object))
    138            memberList.add(object);
    139    }
    140
    141    /**
    142     * Removes an object from the member list of this group in memory.
    143     *
    144     * @param object
    145     *            the HObject (Group or Dataset) to be removed from the member
    146     *            list.
    147     */
    148    public void removeFromMemberList(HObject object) {
    149        if (memberList != null)
    150            memberList.remove(object);
    151    }
    152
    153    /**
    154     * Returns the list of members of this group. The list is an java.util.List
    155     * containing HObjects.
    156     *
    157     * @return the list of members of this group.
    158     */
    159    public List<HObject> getMemberList() {
    160        FileFormat theFile = this.getFileFormat();
    161
    162        if ((memberList == null) && (theFile != null)) {
    163            int size = Math.min(getNumberOfMembersInFile(), this.getFileFormat().getMaxMembers());
    164            memberList = new Vector<>(size + 5); // avoid infinite loop search for groups without members
    165
    166            // find the memberList from the file by checking the group path and
    167            // name. group may be created out of the structure tree
    168            // (H4/5File.loadTree()).
    169            if (theFile.getFID() < 0) {
    170                try {
    171                    theFile.open();
    172                } // load the file structure;
    173                catch (Exception ex) {
    174                    ;
    175                }
    176            }
    177
    178            HObject root = theFile.getRootObject();
    179            if (root == null) return memberList;
    180
    181            Iterator<HObject> it = ((Group) root).depthFirstMemberList().iterator();
    182            Group g = null;
    183            Object uObj = null;
    184            while (it.hasNext()) {
    185                uObj = it.next();
    186
    187                if (uObj instanceof Group) {
    188                    g = (Group) uObj;
    189                    if (g.getPath() != null) { // add this check to get rid of null exception
    190                        if ((this.isRoot() && g.isRoot()) || (this.getPath().equals(g.getPath()) &&
    191                                        g.getName().endsWith(this.getName()))) {
    192                            memberList = g.getMemberList();
    193                            break;
    194                        }
    195                    }
    196                }
    197            }
    198        }
    199
    200        return memberList;
    201    }
    202
    203    /**
    204     * @return the members of this Group in breadth-first order.
    205     */
    206    public List<HObject> breadthFirstMemberList() {
    207        Vector<HObject> members = new Vector<>();
    208        Queue<HObject> queue = new LinkedList<>();
    209        HObject currentObj = this;
    210
    211        queue.addAll(((Group) currentObj).getMemberList());
    212
    213        while(!queue.isEmpty()) {
    214            currentObj = queue.remove();
    215            members.add(currentObj);
    216
    217            if(currentObj instanceof Group && ((Group) currentObj).getNumberOfMembersInFile() > 0)
    218                queue.addAll(((Group) currentObj).getMemberList());
    219        }
    220
    221        return members;
    222    }
    223
    224    /**
    225     * @return the members of this Group in depth-first order.
    226     */
    227    public List<HObject> depthFirstMemberList() {
    228        Vector<HObject> members = new Vector<>();
    229        Stack<HObject> stack = new Stack<>();
    230        HObject currentObj = this;
    231
    232        // Push elements onto the stack in reverse order
    233        List<HObject> list = ((Group) currentObj).getMemberList();
    234        for(int i = list.size() - 1; i >= 0; i--)
    235            stack.push(list.get(i));
    236
    237        while(!stack.empty()) {
    238            currentObj = stack.pop();
    239            members.add(currentObj);
    240
    241            if(currentObj instanceof Group && ((Group) currentObj).getNumberOfMembersInFile() > 0) {
    242                list = ((Group) currentObj).getMemberList();
    243                for(int i = list.size() - 1; i >= 0; i--)
    244                    stack.push(list.get(i));
    245            }
    246        }
    247
    248        return members;
    249    }
    250
    251    /**
    252     * Sets the name of the group.
    253     *
    254     * setName (String newName) changes the name of the group in memory and
    255     * file.
    256     *
    257     * setName() updates the path in memory for all the objects that are under
    258     * the group with the new name.
    259     *
    260     * @param newName
    261     *            The new name of the group.
    262     *
    263     * @throws Exception if the name can not be set
    264     */
    265    @Override
    266    public void setName(String newName) throws Exception {
    267        if (newName == null)
    268            throw new IllegalArgumentException("The new name is NULL");
    269
    270        super.setName(newName);
    271
    272        if (memberList != null) {
    273            int n = memberList.size();
    274            HObject theObj = null;
    275            for (int i = 0; i < n; i++) {
    276                theObj = memberList.get(i);
    277                theObj.setPath(this.getPath() + newName + HObject.SEPARATOR);
    278            }
    279        }
    280    }
    281
    282    /** @return the parent group. */
    283    public final Group getParent() {
    284        return parent;
    285    }
    286
    287    /**
    288     * Checks if it is a root group.
    289     *
    290     * @return true if the group is a root group; otherwise, returns false.
    291     */
    292    public final boolean isRoot() {
    293        return (parent == null);
    294    }
    295
    296    /**
    297     * Returns the total number of members of this group in file.
    298     *
    299     * Current Java applications such as HDFView cannot handle files with large
    300     * numbers of objects (1,000,000 or more objects) due to JVM memory
    301     * limitation. The max_members is used so that applications such as HDFView
    302     * will load up to <i>max_members</i> number of objects. If the number of
    303     * objects in file is larger than <i>max_members</i>, only
    304     * <i>max_members</i> are loaded in memory.
    305     *
    306     * getNumberOfMembersInFile() returns the number of objects in this group.
    307     * The number of objects in memory is obtained by getMemberList().size().
    308     *
    309     * @return Total number of members of this group in the file.
    310     */
    311    public int getNumberOfMembersInFile() {
    312        return nMembersInFile;
    313    }
    314
    315    /**
    316     * Get the HObject at the specified index in this Group's member list.
    317     * @param idx The index of the HObject to get.
    318     * @return The HObject at the specified index.
    319     */
    320    public HObject getMember(int idx) {
    321        if(memberList.size() <= 0 || idx >= memberList.size())
    322            return null;
    323
    324        return memberList.get(idx);
    325    }
    326}