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 *
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}