001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017:
018: package org.apache.poi.poifs.filesystem;
019:
020: import java.io.*;
021:
022: import java.util.*;
023:
024: import org.apache.poi.hpsf.ClassID;
025: import org.apache.poi.poifs.dev.POIFSViewable;
026: import org.apache.poi.poifs.property.DirectoryProperty;
027: import org.apache.poi.poifs.property.DocumentProperty;
028: import org.apache.poi.poifs.property.Property;
029:
030: /**
031: * Simple implementation of DirectoryEntry
032: *
033: * @author Marc Johnson (mjohnson at apache dot org)
034: */
035:
036: public class DirectoryNode extends EntryNode implements DirectoryEntry,
037: POIFSViewable {
038:
039: // Map of Entry instances, keyed by their names
040: private Map _entries;
041:
042: // the POIFSFileSystem we belong to
043: private POIFSFileSystem _filesystem;
044:
045: // the path described by this document
046: private POIFSDocumentPath _path;
047:
048: /**
049: * create a DirectoryNode. This method is not public by design; it
050: * is intended strictly for the internal use of this package
051: *
052: * @param property the DirectoryProperty for this DirectoryEntry
053: * @param filesystem the POIFSFileSystem we belong to
054: * @param parent the parent of this entry
055: */
056:
057: DirectoryNode(final DirectoryProperty property,
058: final POIFSFileSystem filesystem, final DirectoryNode parent) {
059: super (property, parent);
060: if (parent == null) {
061: _path = new POIFSDocumentPath();
062: } else {
063: _path = new POIFSDocumentPath(parent._path,
064: new String[] { property.getName() });
065: }
066: _filesystem = filesystem;
067: _entries = new HashMap();
068: Iterator iter = property.getChildren();
069:
070: while (iter.hasNext()) {
071: Property child = (Property) iter.next();
072: Entry childNode = null;
073:
074: if (child.isDirectory()) {
075: childNode = new DirectoryNode(
076: (DirectoryProperty) child, _filesystem, this );
077: } else {
078: childNode = new DocumentNode((DocumentProperty) child,
079: this );
080: }
081: _entries.put(childNode.getName(), childNode);
082: }
083: }
084:
085: /**
086: * @return this directory's path representation
087: */
088:
089: public POIFSDocumentPath getPath() {
090: return _path;
091: }
092:
093: /**
094: * create a new DocumentEntry
095: *
096: * @param document the new document
097: *
098: * @return the new DocumentEntry
099: *
100: * @exception IOException
101: */
102:
103: DocumentEntry createDocument(final POIFSDocument document)
104: throws IOException {
105: DocumentProperty property = document.getDocumentProperty();
106: DocumentNode rval = new DocumentNode(property, this );
107:
108: ((DirectoryProperty) getProperty()).addChild(property);
109: _filesystem.addDocument(document);
110: _entries.put(property.getName(), rval);
111: return rval;
112: }
113:
114: /**
115: * Change a contained Entry's name
116: *
117: * @param oldName the original name
118: * @param newName the new name
119: *
120: * @return true if the operation succeeded, else false
121: */
122:
123: boolean changeName(final String oldName, final String newName) {
124: boolean rval = false;
125: EntryNode child = (EntryNode) _entries.get(oldName);
126:
127: if (child != null) {
128: rval = ((DirectoryProperty) getProperty()).changeName(child
129: .getProperty(), newName);
130: if (rval) {
131: _entries.remove(oldName);
132: _entries.put(child.getProperty().getName(), child);
133: }
134: }
135: return rval;
136: }
137:
138: /**
139: * Delete an entry
140: *
141: * @param entry the EntryNode to be deleted
142: *
143: * @return true if the entry was deleted, else false
144: */
145:
146: boolean deleteEntry(final EntryNode entry) {
147: boolean rval = ((DirectoryProperty) getProperty())
148: .deleteChild(entry.getProperty());
149:
150: if (rval) {
151: _entries.remove(entry.getName());
152: _filesystem.remove(entry);
153: }
154: return rval;
155: }
156:
157: /* ********** START implementation of DirectoryEntry ********** */
158:
159: /**
160: * get an iterator of the Entry instances contained directly in
161: * this instance (in other words, children only; no grandchildren
162: * etc.)
163: *
164: * @return iterator; never null, but hasNext() may return false
165: * immediately (i.e., this DirectoryEntry is empty). All
166: * objects retrieved by next() are guaranteed to be
167: * implementations of Entry.
168: */
169:
170: public Iterator getEntries() {
171: return _entries.values().iterator();
172: }
173:
174: /**
175: * is this DirectoryEntry empty?
176: *
177: * @return true if this instance contains no Entry instances
178: */
179:
180: public boolean isEmpty() {
181: return _entries.isEmpty();
182: }
183:
184: /**
185: * find out how many Entry instances are contained directly within
186: * this DirectoryEntry
187: *
188: * @return number of immediately (no grandchildren etc.) contained
189: * Entry instances
190: */
191:
192: public int getEntryCount() {
193: return _entries.size();
194: }
195:
196: /**
197: * get a specified Entry by name
198: *
199: * @param name the name of the Entry to obtain.
200: *
201: * @return the specified Entry, if it is directly contained in
202: * this DirectoryEntry
203: *
204: * @exception FileNotFoundException if no Entry with the specified
205: * name exists in this DirectoryEntry
206: */
207:
208: public Entry getEntry(final String name)
209: throws FileNotFoundException {
210: Entry rval = null;
211:
212: if (name != null) {
213: rval = (Entry) _entries.get(name);
214: }
215: if (rval == null) {
216:
217: // either a null name was given, or there is no such name
218: throw new FileNotFoundException("no such entry: \"" + name
219: + "\"");
220: }
221: return rval;
222: }
223:
224: /**
225: * create a new DocumentEntry
226: *
227: * @param name the name of the new DocumentEntry
228: * @param stream the InputStream from which to create the new
229: * DocumentEntry
230: *
231: * @return the new DocumentEntry
232: *
233: * @exception IOException
234: */
235:
236: public DocumentEntry createDocument(final String name,
237: final InputStream stream) throws IOException {
238: return createDocument(new POIFSDocument(name, stream));
239: }
240:
241: /**
242: * create a new DocumentEntry; the data will be provided later
243: *
244: * @param name the name of the new DocumentEntry
245: * @param size the size of the new DocumentEntry
246: * @param writer the writer of the new DocumentEntry
247: *
248: * @return the new DocumentEntry
249: *
250: * @exception IOException
251: */
252:
253: public DocumentEntry createDocument(final String name,
254: final int size, final POIFSWriterListener writer)
255: throws IOException {
256: return createDocument(new POIFSDocument(name, size, _path,
257: writer));
258: }
259:
260: /**
261: * create a new DirectoryEntry
262: *
263: * @param name the name of the new DirectoryEntry
264: *
265: * @return the new DirectoryEntry
266: *
267: * @exception IOException
268: */
269:
270: public DirectoryEntry createDirectory(final String name)
271: throws IOException {
272: DirectoryProperty property = new DirectoryProperty(name);
273: DirectoryNode rval = new DirectoryNode(property, _filesystem,
274: this );
275:
276: ((DirectoryProperty) getProperty()).addChild(property);
277: _filesystem.addDirectory(property);
278: _entries.put(name, rval);
279: return rval;
280: }
281:
282: /**
283: * Gets the storage clsid of the directory entry
284: *
285: * @return storage Class ID
286: */
287: public ClassID getStorageClsid() {
288: return getProperty().getStorageClsid();
289: }
290:
291: /**
292: * Sets the storage clsid for the directory entry
293: *
294: * @param clsidStorage storage Class ID
295: */
296: public void setStorageClsid(ClassID clsidStorage) {
297: getProperty().setStorageClsid(clsidStorage);
298: }
299:
300: /* ********** END implementation of DirectoryEntry ********** */
301: /* ********** START implementation of Entry ********** */
302:
303: /**
304: * is this a DirectoryEntry?
305: *
306: * @return true if the Entry is a DirectoryEntry, else false
307: */
308:
309: public boolean isDirectoryEntry() {
310: return true;
311: }
312:
313: /* ********** END implementation of Entry ********** */
314: /* ********** START extension of Entry ********** */
315:
316: /**
317: * extensions use this method to verify internal rules regarding
318: * deletion of the underlying store.
319: *
320: * @return true if it's ok to delete the underlying store, else
321: * false
322: */
323:
324: protected boolean isDeleteOK() {
325:
326: // if this directory is empty, we can delete it
327: return isEmpty();
328: }
329:
330: /* ********** END extension of Entry ********** */
331: /* ********** START begin implementation of POIFSViewable ********** */
332:
333: /**
334: * Get an array of objects, some of which may implement
335: * POIFSViewable
336: *
337: * @return an array of Object; may not be null, but may be empty
338: */
339:
340: public Object[] getViewableArray() {
341: return new Object[0];
342: }
343:
344: /**
345: * Get an Iterator of objects, some of which may implement
346: * POIFSViewable
347: *
348: * @return an Iterator; may not be null, but may have an empty
349: * back end store
350: */
351:
352: public Iterator getViewableIterator() {
353: List components = new ArrayList();
354:
355: components.add(getProperty());
356: SortedMap sortedEntries = new TreeMap(_entries);
357: Iterator iter = sortedEntries.values().iterator();
358:
359: while (iter.hasNext()) {
360: components.add(iter.next());
361: }
362: return components.iterator();
363: }
364:
365: /**
366: * Give viewers a hint as to whether to call getViewableArray or
367: * getViewableIterator
368: *
369: * @return true if a viewer should call getViewableArray, false if
370: * a viewer should call getViewableIterator
371: */
372:
373: public boolean preferArray() {
374: return false;
375: }
376:
377: /**
378: * Provides a short description of the object, to be used when a
379: * POIFSViewable object has not provided its contents.
380: *
381: * @return short description
382: */
383:
384: public String getShortDescription() {
385: return getName();
386: }
387:
388: /* ********** END begin implementation of POIFSViewable ********** */
389: } // end public class DirectoryNode
|