1 16 package org.apache.cocoon.generation; 17 18 import org.apache.avalon.framework.parameters.Parameters; 19 import org.apache.cocoon.ProcessingException; 20 import org.apache.cocoon.ResourceNotFoundException; 21 import org.apache.cocoon.caching.CacheableProcessingComponent; 22 import org.apache.cocoon.components.source.SourceUtil; 23 import org.apache.cocoon.environment.SourceResolver; 24 import org.apache.excalibur.source.Source; 25 import org.apache.excalibur.source.SourceException; 26 import org.apache.excalibur.source.SourceValidity; 27 import org.apache.regexp.RE; 28 import org.apache.regexp.RESyntaxException; 29 import org.xml.sax.SAXException ; 30 import org.xml.sax.helpers.AttributesImpl ; 31 32 import java.io.File ; 33 import java.io.IOException ; 34 import java.io.Serializable ; 35 import java.net.URL ; 36 import java.text.SimpleDateFormat ; 37 import java.util.ArrayList ; 38 import java.util.Date ; 39 import java.util.List ; 40 import java.util.Map ; 41 import java.util.Stack ; 42 import java.util.Arrays ; 43 import java.util.Comparator ; 44 45 64 public class DirectoryGenerator 65 extends ServiceableGenerator 66 implements CacheableProcessingComponent { 67 68 69 private static final String FILE = "file:"; 70 71 72 protected static final String URI = "http://apache.org/cocoon/directory/2.0"; 73 74 75 protected static final String PREFIX = "dir"; 76 77 78 protected static final String DIR_NODE_NAME = "directory"; 79 protected static final String FILE_NODE_NAME = "file"; 80 81 protected static final String FILENAME_ATTR_NAME = "name"; 82 protected static final String LASTMOD_ATTR_NAME = "lastModified"; 83 protected static final String DATE_ATTR_NAME = "date"; 84 protected static final String SIZE_ATTR_NAME = "size"; 85 86 87 protected DirValidity validity; 88 89 protected AttributesImpl attributes; 90 91 98 protected List cacheKeyParList; 99 100 101 protected int depth; 102 107 protected SimpleDateFormat dateFormatter; 108 109 protected long refreshDelay; 110 116 protected String sort; 117 118 protected boolean reverse; 119 120 protected RE rootRE; 121 122 protected RE includeRE; 123 124 protected RE excludeRE; 125 129 protected boolean isRequestedDirectory; 130 131 132 protected Source directorySource; 133 134 142 public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) 143 throws ProcessingException, SAXException , IOException { 144 if (src == null) { 145 throw new ProcessingException("No src attribute pointing to a directory to be XMLized specified."); 146 } 147 super.setup(resolver, objectModel, src, par); 148 149 try { 150 this.directorySource = this.resolver.resolveURI(src); 151 } catch (SourceException se) { 152 throw SourceUtil.handle(se); 153 } 154 155 this.cacheKeyParList = new ArrayList (); 156 this.cacheKeyParList.add(this.directorySource.getURI()); 157 158 this.depth = par.getParameterAsInteger("depth", 1); 159 this.cacheKeyParList.add(String.valueOf(this.depth)); 160 161 String dateFormatString = par.getParameter("dateFormat", null); 162 this.cacheKeyParList.add(dateFormatString); 163 if (dateFormatString != null) { 164 this.dateFormatter = new SimpleDateFormat (dateFormatString); 165 } else { 166 this.dateFormatter = new SimpleDateFormat (); 167 } 168 169 this.sort = par.getParameter("sort", "name"); 170 this.cacheKeyParList.add(this.sort); 171 172 this.reverse = par.getParameterAsBoolean("reverse", false); 173 this.cacheKeyParList.add(String.valueOf(this.reverse)); 174 175 this.refreshDelay = par.getParameterAsLong("refreshDelay", 1L) * 1000L; 176 this.cacheKeyParList.add(String.valueOf(this.refreshDelay)); 177 178 if (this.getLogger().isDebugEnabled()) { 179 this.getLogger().debug("depth: " + this.depth); 180 this.getLogger().debug("dateFormat: " + this.dateFormatter.toPattern()); 181 this.getLogger().debug("sort: " + this.sort); 182 this.getLogger().debug("reverse: " + this.reverse); 183 this.getLogger().debug("refreshDelay: " + this.refreshDelay); 184 } 185 186 String rePattern = null; 187 try { 188 rePattern = par.getParameter("root", null); 189 this.cacheKeyParList.add(rePattern); 190 this.rootRE = (rePattern == null) ? null : new RE(rePattern); 191 if (this.getLogger().isDebugEnabled()) { 192 this.getLogger().debug("root pattern: " + rePattern); 193 } 194 195 rePattern = par.getParameter("include", null); 196 this.cacheKeyParList.add(rePattern); 197 this.includeRE = (rePattern == null) ? null : new RE(rePattern); 198 if (this.getLogger().isDebugEnabled()) { 199 this.getLogger().debug("include pattern: " + rePattern); 200 } 201 202 rePattern = par.getParameter("exclude", null); 203 this.cacheKeyParList.add(rePattern); 204 this.excludeRE = (rePattern == null) ? null : new RE(rePattern); 205 if (this.getLogger().isDebugEnabled()) { 206 this.getLogger().debug("exclude pattern: " + rePattern); 207 } 208 } catch (RESyntaxException rese) { 209 throw new ProcessingException("Syntax error in regexp pattern '" 210 + rePattern + "'", rese); 211 } 212 213 this.isRequestedDirectory = false; 214 this.attributes = new AttributesImpl (); 215 } 216 217 220 public Serializable getKey() { 221 StringBuffer buffer = new StringBuffer (); 222 int len = this.cacheKeyParList.size(); 223 for (int i = 0; i < len; i++) { 224 buffer.append((String )this.cacheKeyParList.get(i) + ":"); 225 } 226 return buffer.toString(); 227 } 228 229 237 public SourceValidity getValidity() { 238 if (this.validity == null) { 239 this.validity = new DirValidity(this.refreshDelay); 240 } 241 return this.validity; 242 } 243 244 250 public void generate() throws SAXException , ProcessingException { 251 try { 252 String systemId = this.directorySource.getURI(); 253 if (!systemId.startsWith(FILE)) { 254 throw new ResourceNotFoundException(systemId + " does not denote a directory"); 255 } 256 File directoryFile = new File (new URL (systemId).getFile()); 258 if (!directoryFile.isDirectory()) { 259 throw new ResourceNotFoundException(super.source + " is not a directory."); 260 } 261 262 this.contentHandler.startDocument(); 263 this.contentHandler.startPrefixMapping(PREFIX, URI); 264 265 Stack ancestors = getAncestors(directoryFile); 266 addAncestorPath(directoryFile, ancestors); 267 268 this.contentHandler.endPrefixMapping(PREFIX); 269 this.contentHandler.endDocument(); 270 } catch (IOException ioe) { 271 throw new ResourceNotFoundException("Could not read directory " + super.source, ioe); 272 } 273 } 274 275 281 protected Stack getAncestors(File path) { 282 File parent = path; 283 Stack ancestors = new Stack (); 284 285 while ((parent != null) && !isRoot(parent)) { 286 parent = parent.getParentFile(); 287 if (parent != null) { 288 ancestors.push(parent); 289 } else { 290 ancestors.clear(); 292 } 293 } 294 295 return ancestors; 296 } 297 298 306 protected void addAncestorPath(File path, Stack ancestors) throws SAXException { 307 if (ancestors.empty()) { 308 this.isRequestedDirectory = true; 309 addPath(path, depth); 310 } else { 311 startNode(DIR_NODE_NAME, (File )ancestors.pop()); 312 addAncestorPath(path, ancestors); 313 endNode(DIR_NODE_NAME); 314 } 315 } 316 317 326 protected void addPath(File path, int depth) throws SAXException { 327 if (path.isDirectory()) { 328 startNode(DIR_NODE_NAME, path); 329 if (depth > 0) { 330 File contents[] = path.listFiles(); 331 332 if (sort.equals("name")) { 333 Arrays.sort(contents, new Comparator () { 334 public int compare(Object o1, Object o2) { 335 if (reverse) { 336 return ((File )o2).getName().compareTo(((File )o1).getName()); 337 } 338 return ((File )o1).getName().compareTo(((File )o2).getName()); 339 } 340 }); 341 } else if (sort.equals("size")) { 342 Arrays.sort(contents, new Comparator () { 343 public int compare(Object o1, Object o2) { 344 if (reverse) { 345 return new Long (((File )o2).length()).compareTo( 346 new Long (((File )o1).length())); 347 } 348 return new Long (((File )o1).length()).compareTo( 349 new Long (((File )o2).length())); 350 } 351 }); 352 } else if (sort.equals("lastmodified")) { 353 Arrays.sort(contents, new Comparator () { 354 public int compare(Object o1, Object o2) { 355 if (reverse) { 356 return new Long (((File )o2).lastModified()).compareTo( 357 new Long (((File )o1).lastModified())); 358 } 359 return new Long (((File )o1).lastModified()).compareTo( 360 new Long (((File )o2).lastModified())); 361 } 362 }); 363 } else if (sort.equals("directory")) { 364 Arrays.sort(contents, new Comparator () { 365 public int compare(Object o1, Object o2) { 366 File f1 = (File )o1; 367 File f2 = (File )o2; 368 369 if (reverse) { 370 if (f2.isDirectory() && f1.isFile()) 371 return -1; 372 if (f2.isFile() && f1.isDirectory()) 373 return 1; 374 return f2.getName().compareTo(f1.getName()); 375 } 376 if (f2.isDirectory() && f1.isFile()) 377 return 1; 378 if (f2.isFile() && f1.isDirectory()) 379 return -1; 380 return f1.getName().compareTo(f2.getName()); 381 } 382 }); 383 } 384 385 for (int i = 0; i < contents.length; i++) { 386 if (isIncluded(contents[i]) && !isExcluded(contents[i])) { 387 addPath(contents[i], depth - 1); 388 } 389 } 390 } 391 endNode(DIR_NODE_NAME); 392 } else { 393 if (isIncluded(path) && !isExcluded(path)) { 394 startNode(FILE_NODE_NAME, path); 395 endNode(FILE_NODE_NAME); 396 } 397 } 398 } 399 400 407 protected void startNode(String nodeName, File path) throws SAXException { 408 if (this.validity != null) { 409 this.validity.addFile(path); 410 } 411 setNodeAttributes(path); 412 super.contentHandler.startElement(URI, nodeName, PREFIX + ':' + nodeName, attributes); 413 } 414 415 423 protected void setNodeAttributes(File path) throws SAXException { 424 long lastModified = path.lastModified(); 425 attributes.clear(); 426 attributes.addAttribute("", FILENAME_ATTR_NAME, FILENAME_ATTR_NAME, 427 "CDATA", path.getName()); 428 attributes.addAttribute("", LASTMOD_ATTR_NAME, LASTMOD_ATTR_NAME, 429 "CDATA", Long.toString(path.lastModified())); 430 attributes.addAttribute("", DATE_ATTR_NAME, DATE_ATTR_NAME, 431 "CDATA", dateFormatter.format(new Date (lastModified))); 432 attributes.addAttribute("", SIZE_ATTR_NAME, SIZE_ATTR_NAME, 433 "CDATA", Long.toString(path.length())); 434 if (this.isRequestedDirectory) { 435 attributes.addAttribute("", "sort", "sort", "CDATA", this.sort); 436 attributes.addAttribute("", "reverse", "reverse", "CDATA", 437 String.valueOf(this.reverse)); 438 attributes.addAttribute("", "requested", "requested", "CDATA", "true"); 439 this.isRequestedDirectory = false; 440 } 441 } 442 443 449 protected void endNode(String nodeName) throws SAXException { 450 super.contentHandler.endElement(URI, nodeName, PREFIX + ':' + nodeName); 451 } 452 453 460 protected boolean isRoot(File path) { 461 return (this.rootRE == null) ? true : this.rootRE.match(path.getName()); 462 } 463 464 471 protected boolean isIncluded(File path) { 472 return (this.includeRE == null) ? true : this.includeRE.match(path.getName()); 473 } 474 475 482 protected boolean isExcluded(File path) { 483 return (this.excludeRE == null) ? false : this.excludeRE.match(path.getName()); 484 } 485 486 489 public void recycle() { 490 if ( this.resolver != null ) { 491 this.resolver.release(this.directorySource); 492 this.directorySource = null; 493 } 494 this.cacheKeyParList = null; 495 this.attributes = null; 496 this.dateFormatter = null; 497 this.rootRE = null; 498 this.includeRE = null; 499 this.excludeRE = null; 500 this.validity = null; 501 super.recycle(); 502 } 503 504 505 public static class DirValidity implements SourceValidity { 506 507 private long expiry; 508 private long delay; 509 List files = new ArrayList (); 510 List fileDates = new ArrayList (); 511 512 public DirValidity(long delay) { 513 expiry = System.currentTimeMillis() + delay; 514 this.delay = delay; 515 } 516 517 public int isValid() { 518 if (System.currentTimeMillis() <= expiry) { 519 return 1; 520 } 521 522 expiry = System.currentTimeMillis() + delay; 523 int len = files.size(); 524 for (int i = 0; i < len; i++) { 525 File f = (File )files.get(i); 526 if (!f.exists()) { 527 return -1; } 529 530 long oldDate = ((Long )fileDates.get(i)).longValue(); 531 long newDate = f.lastModified(); 532 533 if (oldDate != newDate) { 534 return -1; 535 } 536 } 537 538 expiry = System.currentTimeMillis() + delay; 540 return 1; 541 } 542 543 public int isValid(SourceValidity newValidity) { 544 return isValid(); 545 } 546 547 public void addFile(File f) { 548 files.add(f); 549 fileDates.add(new Long (f.lastModified())); 550 } 551 } 552 } 553 | Popular Tags |