1 27 28 package org.enhydra.xml.xmlc.deferredparsing; 29 30 import java.io.File ; 31 import java.io.IOException ; 32 import java.io.PrintWriter ; 33 import java.lang.reflect.Field ; 34 import java.net.MalformedURLException ; 35 import java.net.URL ; 36 import java.util.ArrayList ; 37 import java.util.Date ; 38 import java.util.Iterator ; 39 import java.util.Map ; 40 import java.util.WeakHashMap ; 41 42 import org.enhydra.xml.io.ErrorReporter; 43 import org.enhydra.xml.xmlc.XMLCError; 44 import org.enhydra.xml.xmlc.XMLCException; 45 import org.enhydra.xml.xmlc.XMLCLogger; 46 import org.enhydra.xml.xmlc.XMLCRuntimeException; 47 import org.enhydra.xml.xmlc.XMLObject; 48 import org.enhydra.xml.xmlc.compiler.EditDOM; 49 import org.enhydra.xml.xmlc.compiler.Parse; 50 import org.enhydra.xml.xmlc.dom.XMLCDocument; 51 import org.enhydra.xml.xmlc.metadata.DocumentClass; 52 import org.enhydra.xml.xmlc.metadata.MetaData; 53 import org.enhydra.xml.xmlc.metadata.MetaDataDocument; 54 import org.w3c.dom.DOMImplementation ; 55 import org.w3c.dom.Document ; 56 import org.w3c.dom.DocumentType ; 57 import org.w3c.dom.Node ; 58 import org.xml.sax.InputSource ; 59 60 76 public class DocumentLoaderImpl implements DocumentLoader { 77 78 79 final private static String META_FILE_EXT = ".xmlc"; 80 81 82 private XMLCDeferredParsingFactory factory; 83 84 85 protected static class CacheEntry { 86 volatile long timestamp = -1; 87 volatile Document document = null; 88 volatile URL src = null; 89 volatile boolean ready = false; 90 } 91 92 93 private Map templateCache; 94 95 100 public DocumentLoaderImpl() { 101 this(null); 102 } 103 104 108 public DocumentLoaderImpl(Map cache) { 109 if (cache == null) { 110 templateCache = new Cache(); 111 } else { 112 templateCache = cache; 113 } 114 } 115 116 129 protected CacheEntry getCacheEntry(Class docClass) { 130 String name = docClass.getName(); 131 132 146 CacheEntry entry = (CacheEntry) templateCache.get(name); 147 if (entry == null) { 148 synchronized (templateCache) { 149 entry = (CacheEntry) templateCache.get(name); 150 if (entry == null) { 151 152 if (getLogger().debugEnabled()) { 153 getLogger().logDebug( 154 ">>>Creating cache entry for " 155 + docClass.getName()); 156 } 157 158 entry = new CacheEntry(); 159 templateCache.put(name, entry); 160 } 161 } 162 } 163 164 if (!entry.ready) { 165 synchronized (entry) { 166 if (!entry.ready) { 167 entry.src = findSourceUrl(docClass); 168 entry.document = parseDocument(docClass, entry.src); 169 entry.timestamp = getCurrentTimestamp(docClass, entry.src); 170 entry.ready = true; 171 } 172 } 173 } 174 175 return entry; 176 } 177 178 181 private CacheEntry checkCacheEntry(Class docClass, CacheEntry entry) { 182 183 long ts = getCurrentTimestamp(docClass, entry.src); 185 186 if (getLogger().debugEnabled()) { 187 getLogger().logDebug(">>>Checking time stamp ts=" + new Date (ts)); 188 getLogger().logDebug(">>> entry.timestamp=" + new Date (entry.timestamp)); 189 } 190 191 if (ts == -1 || ts > entry.timestamp) { 192 synchronized (entry) { 193 ts = getCurrentTimestamp(docClass, entry.src); 194 if (ts == -1 || ts > entry.timestamp) { 197 entry.document = parseDocument(docClass, entry.src); 198 entry.timestamp = getCurrentTimestamp(docClass, entry.src); 199 } else { 200 if (getLogger().debugEnabled()) { 201 getLogger().logDebug(">>>DOM for " 202 + docClass.getName() 203 + " has been updated by another thread."); 204 } 205 } 206 } 207 } 208 209 return entry; 210 } 211 212 217 public Document getDocument(Class docClass) throws XMLCRuntimeException { 218 if (getLogger().debugEnabled()) { 219 getLogger().logDebug(">>>DOM instance requested for class " + docClass.getName()); 220 } 221 222 CacheEntry entry = getCacheEntry(docClass); 224 entry = checkCacheEntry(docClass, entry); 225 227 if (getLogger().debugEnabled()) { 228 getLogger().logDebug(">>>Document class " + entry.document.getClass().getName()); 229 } 230 231 if (entry.document.getDoctype() == null) { 232 return (Document) entry.document.cloneNode(true); 235 } else { 236 DOMImplementation domImpl = entry.document.getImplementation(); 239 String documentElement = entry.document.getDoctype().getName(); 244 DocumentType docType = domImpl.createDocumentType(documentElement, entry.document.getDoctype().getPublicId(), entry.document.getDoctype().getSystemId()); 247 Document doc = domImpl.createDocument("", documentElement, docType); 248 Node node = doc.importNode(entry.document.getDocumentElement(), true); 249 doc.replaceChild(node, doc.getDocumentElement()); 250 return doc; 251 } 252 } 253 254 258 public static Object getClassConstant( 259 Class xmlcBasedClass, 260 String constName) { 261 try { 263 Field field = xmlcBasedClass.getField(constName); 264 return field.get(null); 265 } catch (NoSuchFieldException except) { 266 throw new XMLCRuntimeException("Couldn't find class constant \"" 267 + constName 268 + "\" in class \"" 269 + xmlcBasedClass.getName() 270 + "\", maybe be a XMLC generated class or descendent", 271 except); 272 } catch (IllegalAccessException except) { 273 throw new XMLCRuntimeException(except); 274 } 275 } 276 277 280 protected String getSourceFileName(Class docClass) 281 throws XMLCRuntimeException { 282 String srcPath = 283 (String ) getClassConstant(docClass, 284 XMLObject.XMLC_SOURCE_FILE_FIELD_NAME); 285 if (srcPath == null) { 286 throw new XMLCRuntimeException( 287 XMLObject.XMLC_SOURCE_FILE_FIELD_NAME 288 + " is null in class " 289 + docClass.getName()); 290 } 291 if (srcPath.startsWith("/")) { 292 srcPath = srcPath.substring(1); 293 } 294 return srcPath; 295 } 296 297 301 private URL getDocURLFromClasspath(String path, ClassLoader loader) { 302 URL srcURL = loader.getResource(path); 303 304 if ((srcURL != null) && getLogger().debugEnabled()) { 305 getLogger().logDebug( 306 ">>>Get document '" + srcURL + "' from classpath"); 307 } 308 309 return srcURL; 310 } 311 312 317 private URL getDocURLFromResourceDir(String path) { 318 File sourceFile = null; 319 File dir = null; 320 321 for (Iterator iter = getFactory().getResourceDirList().iterator(); 322 iter.hasNext(); 323 ) { 324 dir = (File ) iter.next(); 325 sourceFile = new File (dir, path); 326 327 if (sourceFile.exists()) { 328 break; 329 } 330 331 sourceFile = null; 332 } 333 334 if (sourceFile != null) { 335 try { 336 return sourceFile.toURL(); 337 } catch (MalformedURLException e) { 338 throw new XMLCError("Cannot construct URL for file " + sourceFile, e); 339 } 340 } else { 341 return null; 342 } 343 } 344 345 349 private String [] getPathFromPackagePrefix(String srcPath) { 350 ArrayList list = new ArrayList (); 351 list.add(srcPath); 352 353 for (Iterator iter = getFactory().getPackagePrefixList().iterator(); 354 iter.hasNext(); 355 ) { 356 String prefix = (String ) iter.next(); 357 if (srcPath.startsWith(prefix)) { 358 list.add(srcPath.substring(prefix.length() + 1)); 359 break; 360 } 361 } 362 363 return (String []) list.toArray(new String [0]); 364 } 365 366 369 protected URL findSourceUrl(Class docClass) throws XMLCRuntimeException { 370 URL sourceDoc = null; 371 String srcPath = getSourceFileName(docClass); 372 373 String [] path = getPathFromPackagePrefix(srcPath); 374 375 for (int i = 0; i < path.length; i++) { 376 sourceDoc = getDocURLFromResourceDir(path[i]); 381 if (sourceDoc == null) { 382 sourceDoc = 383 getDocURLFromClasspath(path[i], docClass.getClassLoader()); 384 } 385 386 if (sourceDoc != null) { 387 break; 388 } 389 } 390 391 if (sourceDoc == null) { 392 throw new XMLCRuntimeException( 393 "Source file '" 394 + srcPath 395 + "' not found for " 396 + docClass.getName()); 397 } 398 399 return sourceDoc; 400 } 401 402 406 protected String metaDataFileName(Class docClass) 407 throws XMLCRuntimeException { 408 String fileName = docClass.getName().replace('.', '/'); 410 411 if (fileName.endsWith(DocumentClass.IMPLEMENTATION_SUFFIX)) { 412 fileName = 413 fileName.substring( 414 0, 415 fileName.length() 416 - DocumentClass.IMPLEMENTATION_SUFFIX.length()); 417 } 418 419 return fileName + META_FILE_EXT; 420 } 421 422 427 protected MetaData loadMetaData( 428 Class docClass, 429 ErrorReporter errorReporter) 430 throws XMLCException { 431 String srcPath = metaDataFileName(docClass); 432 if (getLogger().debugEnabled()) { 433 getLogger().logDebug( 434 ">>>Loading metadata '" 435 + srcPath 436 + "' for " 437 + docClass.getName()); 438 } 439 440 URL metaFile = getDocURLFromResourceDir(srcPath); 445 if (metaFile == null) { 446 metaFile = getDocURLFromClasspath(srcPath, docClass.getClassLoader()); 447 if (metaFile == null) { 448 String defaultMetaFile = factory.getDefaultMetaDataFile(); 449 if (defaultMetaFile != null) { 450 metaFile = getDocURLFromResourceDir(defaultMetaFile); 451 if (metaFile == null) { 452 metaFile = 453 getDocURLFromClasspath( 454 defaultMetaFile, 455 docClass.getClassLoader()); 456 } 457 } 458 } 459 } 460 461 if (metaFile == null) { 462 throw new XMLCRuntimeException( 463 "Metadat file '" 464 + srcPath 465 + "' not found for " 466 + docClass.getName()); 467 } 468 469 if (getLogger().debugEnabled()) { 470 getLogger().logDebug( 471 ">>>Load MetaData from " + metaFile); 472 } 473 474 MetaDataDocument metaDataDoc = 475 MetaDataDocument.parseMetaData( 476 new InputSource (metaFile.toString()), 477 errorReporter, 478 docClass.getClassLoader()); 479 return metaDataDoc.getMetaData(); 480 } 481 482 489 protected long getCurrentTimestamp(Class docClass, URL src) { 490 try { 491 return src.openConnection().getLastModified(); 492 } catch (IOException e) { 493 getLogger().logInfo("Cannot read last modified time for " + src, e); 494 return -1; 495 } 496 } 497 498 503 protected Document parseDocument(Class docClass, URL src) { 504 ErrorReporter errorReporter = new ErrorReporter(); 505 try { 506 if (getLogger().debugEnabled()) { 507 getLogger().logDebug( 508 ">>>Parsing DOM for " 509 + docClass.getName() 510 + " from source URL " 511 + src.toString()); 512 } 513 514 MetaData metaData = loadMetaData(docClass, errorReporter); 515 metaData.getInputDocument().setUrl(src.toString()); 516 517 Parse parser = createParser(docClass, errorReporter, null); 518 XMLCDocument xmlcDoc = parser.parse(metaData); 519 520 EditDOM domEditor = createDOMEditor(docClass, metaData); 521 domEditor.edit(xmlcDoc); 522 523 return xmlcDoc.getDocument(); 524 } catch (Exception e) { 525 XMLCRuntimeException except; 526 if (e instanceof XMLCRuntimeException) { 527 except = (XMLCRuntimeException) e; 528 } else { 529 except = new XMLCRuntimeException(e); 530 } 531 throw except; 532 } 533 } 534 535 537 protected Parse createParser( 538 Class docClass, 539 ErrorReporter errorReporter, 540 PrintWriter verboseOut) { 541 return new Parse(errorReporter, verboseOut); 542 } 543 544 546 protected EditDOM createDOMEditor(Class docClass, MetaData metaData) { 547 return new EditDOM(metaData); 548 } 549 550 555 public void init(XMLCDeferredParsingFactory factory) { 556 this.factory = factory; 557 } 558 559 560 protected XMLCDeferredParsingFactory getFactory() { 561 return factory; 562 } 563 564 565 protected XMLCLogger getLogger() { 566 return factory.getLogger(); 567 } 568 } 569 | Popular Tags |