1 2 /* ==================================================================== 3 * The Apache Software License, Version 1.1 4 * 5 * Copyright (c) 2002 The Apache Software Foundation. All rights 6 * reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 20 * 3. The end-user documentation included with the redistribution, 21 * if any, must include the following acknowledgment: 22 * "This product includes software developed by the 23 * Apache Software Foundation (http://www.apache.org/)." 24 * Alternately, this acknowledgment may appear in the software itself, 25 * if and wherever such third-party acknowledgments normally appear. 26 * 27 * 4. The names "Apache" and "Apache Software Foundation" and 28 * "Apache POI" must not be used to endorse or promote products 29 * derived from this software without prior written permission. For 30 * written permission, please contact apache@apache.org. 31 * 32 * 5. Products derived from this software may not be called "Apache", 33 * "Apache POI", nor may "Apache" appear in their name, without 34 * prior written permission of the Apache Software Foundation. 35 * 36 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 37 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 38 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 39 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 40 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 41 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 42 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 43 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 44 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 46 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 47 * SUCH DAMAGE. 48 * ==================================================================== 49 * 50 * This software consists of voluntary contributions made by many 51 * individuals on behalf of the Apache Software Foundation. For more 52 * information on the Apache Software Foundation, please see 53 * <http://www.apache.org/>. 54 */ 55 56 package org.apache.poi.poifs.filesystem; 57 58 import java.io.*; 59 60 import java.util.*; 61 62 import org.apache.poi.poifs.dev.POIFSViewable; 63 import org.apache.poi.poifs.property.DirectoryProperty; 64 import org.apache.poi.poifs.property.DocumentProperty; 65 import org.apache.poi.poifs.property.Property; 66 67 /** 68 * Simple implementation of DirectoryEntry 69 * 70 * @author Marc Johnson (mjohnson at apache dot org) 71 */ 72 73 public class DirectoryNode 74 extends EntryNode 75 implements DirectoryEntry, POIFSViewable 76 { 77 78 // Map of Entry instances, keyed by their names 79 private Map _entries; 80 81 // the POIFSFileSystem we belong to 82 private POIFSFileSystem _filesystem; 83 84 // the path described by this document 85 private POIFSDocumentPath _path; 86 87 /** 88 * create a DirectoryNode. This method is not public by design; it 89 * is intended strictly for the internal use of this package 90 * 91 * @param property the DirectoryProperty for this DirectoryEntry 92 * @param filesystem the POIFSFileSystem we belong to 93 * @param parent the parent of this entry 94 */ 95 96 DirectoryNode(final DirectoryProperty property, 97 final POIFSFileSystem filesystem, 98 final DirectoryNode parent) 99 { 100 super(property, parent); 101 if (parent == null) 102 { 103 _path = new POIFSDocumentPath(); 104 } 105 else 106 { 107 _path = new POIFSDocumentPath(parent._path, new String[] 108 { 109 property.getName() 110 }); 111 } 112 _filesystem = filesystem; 113 _entries = new HashMap(); 114 Iterator iter = property.getChildren(); 115 116 while (iter.hasNext()) 117 { 118 Property child = ( Property ) iter.next(); 119 Entry childNode = null; 120 121 if (child.isDirectory()) 122 { 123 childNode = new DirectoryNode(( DirectoryProperty ) child, 124 _filesystem, this); 125 } 126 else 127 { 128 childNode = new DocumentNode(( DocumentProperty ) child, 129 this); 130 } 131 _entries.put(childNode.getName(), childNode); 132 } 133 } 134 135 /** 136 * @return this directory's path representation 137 */ 138 139 public POIFSDocumentPath getPath() 140 { 141 return _path; 142 } 143 144 /** 145 * create a new DocumentEntry 146 * 147 * @param document the new document 148 * 149 * @return the new DocumentEntry 150 * 151 * @exception IOException 152 */ 153 154 DocumentEntry createDocument(final POIFSDocument document) 155 throws IOException 156 { 157 DocumentProperty property = document.getDocumentProperty(); 158 DocumentNode rval = new DocumentNode(property, this); 159 160 (( DirectoryProperty ) getProperty()).addChild(property); 161 _filesystem.addDocument(document); 162 _entries.put(property.getName(), rval); 163 return rval; 164 } 165 166 /** 167 * Change a contained Entry's name 168 * 169 * @param oldName the original name 170 * @param newName the new name 171 * 172 * @return true if the operation succeeded, else false 173 */ 174 175 boolean changeName(final String oldName, final String newName) 176 { 177 boolean rval = false; 178 EntryNode child = ( EntryNode ) _entries.get(oldName); 179 180 if (child != null) 181 { 182 rval = (( DirectoryProperty ) getProperty()) 183 .changeName(child.getProperty(), newName); 184 if (rval) 185 { 186 _entries.remove(oldName); 187 _entries.put(child.getProperty().getName(), child); 188 } 189 } 190 return rval; 191 } 192 193 /** 194 * Delete an entry 195 * 196 * @param entry the EntryNode to be deleted 197 * 198 * @return true if the entry was deleted, else false 199 */ 200 201 boolean deleteEntry(final EntryNode entry) 202 { 203 boolean rval = 204 (( DirectoryProperty ) getProperty()) 205 .deleteChild(entry.getProperty()); 206 207 if (rval) 208 { 209 _entries.remove(entry.getName()); 210 _filesystem.remove(entry); 211 } 212 return rval; 213 } 214 215 /* ********** START implementation of DirectoryEntry ********** */ 216 217 /** 218 * get an iterator of the Entry instances contained directly in 219 * this instance (in other words, children only; no grandchildren 220 * etc.) 221 * 222 * @return iterator; never null, but hasNext() may return false 223 * immediately (i.e., this DirectoryEntry is empty). All 224 * objects retrieved by next() are guaranteed to be 225 * implementations of Entry. 226 */ 227 228 public Iterator getEntries() 229 { 230 return _entries.values().iterator(); 231 } 232 233 /** 234 * is this DirectoryEntry empty? 235 * 236 * @return true if this instance contains no Entry instances 237 */ 238 239 public boolean isEmpty() 240 { 241 return _entries.isEmpty(); 242 } 243 244 /** 245 * find out how many Entry instances are contained directly within 246 * this DirectoryEntry 247 * 248 * @return number of immediately (no grandchildren etc.) contained 249 * Entry instances 250 */ 251 252 public int getEntryCount() 253 { 254 return _entries.size(); 255 } 256 257 /** 258 * get a specified Entry by name 259 * 260 * @param name the name of the Entry to obtain. 261 * 262 * @return the specified Entry, if it is directly contained in 263 * this DirectoryEntry 264 * 265 * @exception FileNotFoundException if no Entry with the specified 266 * name exists in this DirectoryEntry 267 */ 268 269 public Entry getEntry(final String name) 270 throws FileNotFoundException 271 { 272 Entry rval = null; 273 274 if (name != null) 275 { 276 rval = ( Entry ) _entries.get(name); 277 } 278 if (rval == null) 279 { 280 281 // either a null name was given, or there is no such name 282 throw new FileNotFoundException("no such entry: \"" + name 283 + "\""); 284 } 285 return rval; 286 } 287 288 /** 289 * create a new DocumentEntry 290 * 291 * @param name the name of the new DocumentEntry 292 * @param stream the InputStream from which to create the new 293 * DocumentEntry 294 * 295 * @return the new DocumentEntry 296 * 297 * @exception IOException 298 */ 299 300 public DocumentEntry createDocument(final String name, 301 final InputStream stream) 302 throws IOException 303 { 304 return createDocument(new POIFSDocument(name, stream)); 305 } 306 307 /** 308 * create a new DocumentEntry; the data will be provided later 309 * 310 * @param name the name of the new DocumentEntry 311 * @param size the size of the new DocumentEntry 312 * @param writer the writer of the new DocumentEntry 313 * 314 * @return the new DocumentEntry 315 * 316 * @exception IOException 317 */ 318 319 public DocumentEntry createDocument(final String name, final int size, 320 final POIFSWriterListener writer) 321 throws IOException 322 { 323 return createDocument(new POIFSDocument(name, size, _path, writer)); 324 } 325 326 /** 327 * create a new DirectoryEntry 328 * 329 * @param name the name of the new DirectoryEntry 330 * 331 * @return the new DirectoryEntry 332 * 333 * @exception IOException 334 */ 335 336 public DirectoryEntry createDirectory(final String name) 337 throws IOException 338 { 339 DirectoryProperty property = new DirectoryProperty(name); 340 DirectoryNode rval = new DirectoryNode(property, _filesystem, 341 this); 342 343 (( DirectoryProperty ) getProperty()).addChild(property); 344 _filesystem.addDirectory(property); 345 _entries.put(name, rval); 346 return rval; 347 } 348 349 /* ********** END implementation of DirectoryEntry ********** */ 350 /* ********** START implementation of Entry ********** */ 351 352 /** 353 * is this a DirectoryEntry? 354 * 355 * @return true if the Entry is a DirectoryEntry, else false 356 */ 357 358 public boolean isDirectoryEntry() 359 { 360 return true; 361 } 362 363 /* ********** END implementation of Entry ********** */ 364 /* ********** START extension of Entry ********** */ 365 366 /** 367 * extensions use this method to verify internal rules regarding 368 * deletion of the underlying store. 369 * 370 * @return true if it's ok to delete the underlying store, else 371 * false 372 */ 373 374 protected boolean isDeleteOK() 375 { 376 377 // if this directory is empty, we can delete it 378 return isEmpty(); 379 } 380 381 /* ********** END extension of Entry ********** */ 382 /* ********** START begin implementation of POIFSViewable ********** */ 383 384 /** 385 * Get an array of objects, some of which may implement 386 * POIFSViewable 387 * 388 * @return an array of Object; may not be null, but may be empty 389 */ 390 391 public Object [] getViewableArray() 392 { 393 return new Object[ 0 ]; 394 } 395 396 /** 397 * Get an Iterator of objects, some of which may implement 398 * POIFSViewable 399 * 400 * @return an Iterator; may not be null, but may have an empty 401 * back end store 402 */ 403 404 public Iterator getViewableIterator() 405 { 406 List components = new ArrayList(); 407 408 components.add(getProperty()); 409 SortedMap sortedEntries = new TreeMap(_entries); 410 Iterator iter = sortedEntries.values().iterator(); 411 412 while (iter.hasNext()) 413 { 414 components.add(iter.next()); 415 } 416 return components.iterator(); 417 } 418 419 /** 420 * Give viewers a hint as to whether to call getViewableArray or 421 * getViewableIterator 422 * 423 * @return true if a viewer should call getViewableArray, false if 424 * a viewer should call getViewableIterator 425 */ 426 427 public boolean preferArray() 428 { 429 return false; 430 } 431 432 /** 433 * Provides a short description of the object, to be used when a 434 * POIFSViewable object has not provided its contents. 435 * 436 * @return short description 437 */ 438 439 public String getShortDescription() 440 { 441 return getName(); 442 } 443 444 /* ********** END begin implementation of POIFSViewable ********** */ 445 } // end public class DirectoryNode 446 447