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}