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