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