001 /* 002 * Copyright 2005-2006 Stephen J. McConnell 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.library.impl; 020 021 import java.io.File; 022 import java.io.FileNotFoundException; 023 import java.net.URI; 024 import java.net.URISyntaxException; 025 import java.util.ArrayList; 026 import java.util.Properties; 027 import java.util.Hashtable; 028 029 import net.dpml.library.info.LibraryDecoder; 030 import net.dpml.library.info.ImportDirective; 031 import net.dpml.library.info.LibraryDirective; 032 import net.dpml.library.info.ModuleDirective; 033 import net.dpml.library.info.ResourceDirective; 034 import net.dpml.library.info.Scope; 035 import net.dpml.library.Library; 036 import net.dpml.library.Module; 037 import net.dpml.library.ModuleNotFoundException; 038 import net.dpml.library.Resource; 039 import net.dpml.library.ResourceNotFoundException; 040 041 import net.dpml.transit.Artifact; 042 043 import net.dpml.util.Logger; 044 045 /** 046 * Utility class used for construction of a module model from an XML source. 047 * 048 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 049 * @version 1.2.0 050 */ 051 public final class DefaultLibrary extends DefaultDictionary implements Library 052 { 053 private static final LibraryDecoder LIBRARY_DECODER = new LibraryDecoder(); 054 055 private final LibraryDirective m_directive; 056 private final DefaultModule m_module; 057 private final File m_root; 058 private final Logger m_logger; 059 private final Hashtable m_anonymous = new Hashtable(); 060 061 private static LibraryDirective buildLibraryDirective( File source ) throws Exception 062 { 063 return LIBRARY_DECODER.build( source ); 064 } 065 066 /** 067 * Creation of a new library. The definition of the indexwill 068 * be resolved by search up the file system for a file named index.xml. 069 * @param logger the assigned logging channel 070 * @exception Exception if an error occurs during defintion loading 071 */ 072 public DefaultLibrary( Logger logger ) throws Exception 073 { 074 this( logger, resolveLibrarySource() ); 075 } 076 077 /** 078 * Creation of a new library. 079 * @param logger the assigned logging channel 080 * @param source the index source defintion 081 * @exception Exception if an error occurs during defintion loading 082 */ 083 public DefaultLibrary( Logger logger, File source ) throws Exception 084 { 085 super( null, buildLibraryDirective( source ) ); 086 087 if( null == logger ) 088 { 089 throw new NullPointerException( "logger" ); 090 } 091 092 m_logger = logger; 093 m_directive = (LibraryDirective) super.getAbstractDirective(); 094 m_root = source.getParentFile().getCanonicalFile(); 095 m_module = new DefaultModule( this, m_directive ); 096 097 getLogger().debug( "loaded root module: " + m_root ); 098 System.setProperty( "dpml.library.basedir", m_root.toString() ); 099 100 // handle expansion of import directives 101 102 ImportDirective[] imports = m_directive.getImportDirectives(); 103 ModuleDirective[] importModuleDirectives = new ModuleDirective[ imports.length ]; 104 for( int i=0; i<imports.length; i++ ) 105 { 106 ImportDirective include = imports[i]; 107 ImportDirective.Mode mode = include.getMode(); 108 if( ImportDirective.Mode.FILE.equals( mode ) ) 109 { 110 throw new UnsupportedOperationException( "file" ); 111 } 112 else 113 { 114 String path = include.getValue(); 115 URI uri = new URI( path ); 116 getLogger().debug( "loading external import: " + uri ); 117 ResourceDirective resource = LIBRARY_DECODER.buildResource( uri ); 118 if( resource instanceof ModuleDirective ) 119 { 120 ModuleDirective moduleDirective = (ModuleDirective) resource; 121 importModuleDirectives[i] = moduleDirective; 122 } 123 else 124 { 125 final String error = 126 "Not yet equipped to import resource of the type [" 127 + resource.getClass().getName() 128 + "."; 129 throw new IllegalArgumentException( error ); 130 } 131 } 132 } 133 134 for( int i=0; i<importModuleDirectives.length; i++ ) 135 { 136 ModuleDirective importModuleDirective = importModuleDirectives[i]; 137 m_module.addResource( importModuleDirective ); 138 } 139 140 // create the top-level modules 141 142 ArrayList primaryDirectives = new ArrayList(); 143 ResourceDirective[] directives = m_directive.getResourceDirectives(); 144 for( int i=0; i<directives.length; i++ ) 145 { 146 ResourceDirective directive = directives[i]; 147 //if( directive instanceof ModuleDirective ) 148 //{ 149 //ModuleDirective md = (ModuleDirective) directive; 150 primaryDirectives.add( directive ); 151 //} 152 //else 153 //{ 154 // final String error = 155 // "No support in place for non-module top-level resources."; 156 // throw new IllegalArgumentException( error ); 157 //} 158 } 159 ResourceDirective[] values = (ResourceDirective[]) primaryDirectives.toArray( new ResourceDirective[0] ); 160 for( int i=0; i<values.length; i++ ) 161 { 162 ResourceDirective d = values[i]; 163 m_module.addResource( d ); 164 } 165 } 166 167 //---------------------------------------------------------------------------- 168 // Library 169 //---------------------------------------------------------------------------- 170 171 /** 172 * Utility operation to sort a collection of resources. 173 * @param resources the resources to sort 174 * @return the sorted resource array 175 */ 176 public Resource[] sort( Resource[] resources ) 177 { 178 DefaultResource[] array = new DefaultResource[ resources.length ]; 179 for( int i=0; i<resources.length; i++ ) 180 { 181 array[i] = (DefaultResource) resources[i]; 182 } 183 return m_module.sortDefaultResources( array, Scope.TEST ); 184 } 185 186 /** 187 * Return an array of the top-level modules within the library. 188 * @return module array 189 */ 190 public Module[] getModules() 191 { 192 return m_module.getModules(); 193 } 194 195 /** 196 * Return a array of all modules in the library. 197 * @return module array 198 */ 199 public Module[] getAllModules() 200 { 201 return m_module.getAllModules(); 202 } 203 204 /** 205 * Return a named module. 206 * @param ref the fully qualified module name 207 * @return the module 208 * @exception ModuleNotFoundException if the module cannot be found 209 */ 210 public Module getModule( String ref ) throws ModuleNotFoundException 211 { 212 return m_module.getModule( ref ); 213 } 214 215 /** 216 * Recursively lookup a resource using a fully qualified reference. 217 * @param ref the fully qualified resource name 218 * @return the resource instance 219 * @exception ResourceNotFoundException if the resource cannot be found 220 */ 221 public Resource getResource( String ref ) throws ResourceNotFoundException 222 { 223 return m_module.getResource( ref ); 224 } 225 226 /** 227 * <p>Select a set of resource matching a supplied a resource selection 228 * constraint. The constraint may contain the wildcards '**' and '*'. 229 * @param criteria the selection criteria 230 * @param sort if true the returned array will be sorted relative to dependencies 231 * otherwise the array will be sorted alphanumerically with respect to the resource 232 * path 233 * @return an array of resources matching the selction criteria 234 */ 235 public Resource[] select( String criteria, boolean sort ) 236 { 237 return m_module.select( criteria, false, sort ); 238 } 239 240 /** 241 * <p>Select a set of resource matching a supplied a resource selection 242 * constraint. The constraint may contain the wildcards '**' and '*'. 243 * @param criteria the selection criteria 244 * @param local if true restrict selection to local projects 245 * @param sort if true the returned array will be sorted relative to dependencies 246 * otherwise the array will be sorted alphanumerically with respect to the resource 247 * path 248 * @return an array of resources matching the selction criteria 249 */ 250 public Resource[] select( String criteria, boolean local, boolean sort ) 251 { 252 return m_module.select( criteria, local, sort ); 253 } 254 255 /** 256 * Select all local projects with a basedir equal to or deeper than the supplied 257 * directory. 258 * @param base the reference basedir 259 * @return an array of projects within or lower than the supplied basedir 260 */ 261 public Resource[] select( File base ) 262 { 263 return select( base, true ); 264 } 265 266 /** 267 * Select all local projects relative to the supplied basedir. 268 * @param base the reference basedir 269 * @param self if true and the basedir resolves to a project then include the project 270 * otherwise the project will be expluded from selection 271 * @return an array of projects relative to the basedir 272 */ 273 public Resource[] select( final File base, boolean self ) 274 { 275 String root = base.toString(); 276 ArrayList list = new ArrayList(); 277 DefaultResource[] resources = m_module.selectDefaultResources( true, "**/*" ); 278 for( int i=0; i<resources.length; i++ ) 279 { 280 DefaultResource resource = resources[i]; 281 File basedir = resource.getBaseDir(); 282 if( null != basedir ) 283 { 284 String path = basedir.toString(); 285 if( path.startsWith( root ) ) 286 { 287 if( path.equals( root ) ) 288 { 289 if( self ) 290 { 291 list.add( resource ); 292 } 293 } 294 else 295 { 296 list.add( resource ); 297 } 298 } 299 } 300 else 301 { 302 final String error = 303 "Local project list returned a resource with a null basedir [" 304 + resource.getResourcePath() 305 + "]."; 306 throw new IllegalStateException( error ); 307 } 308 } 309 DefaultResource[] selection = (DefaultResource[]) list.toArray( new DefaultResource[0] ); 310 return m_module.sortDefaultResources( selection ); 311 } 312 313 /** 314 * Locate a resource relative to a base directory. 315 * @param base the base directory 316 * @return a resource with a matching basedir 317 * @exception ResourceNotFoundException if resource match relative to the supplied base 318 */ 319 public Resource locate( File base ) throws ResourceNotFoundException 320 { 321 return m_module.locate( base ); 322 } 323 324 //---------------------------------------------------------------------------- 325 // Dictionary 326 //---------------------------------------------------------------------------- 327 328 /** 329 * Return the property names associated with the dictionary. 330 * @return the array of property names 331 */ 332 public String[] getPropertyNames() 333 { 334 return m_module.getPropertyNames(); 335 } 336 337 /** 338 * Return a property value. 339 * @param key the property key 340 * @return the property value 341 */ 342 public String getProperty( String key ) 343 { 344 return m_module.getProperty( key ); 345 } 346 347 /** 348 * Return a property value. 349 * @param key the property key 350 * @param value the default value 351 * @return the property value 352 */ 353 public String getProperty( String key, String value ) 354 { 355 return m_module.getProperty( key, value ); 356 } 357 //---------------------------------------------------------------------------- 358 // internals 359 //---------------------------------------------------------------------------- 360 361 /** 362 * Construct a new locally referenced anonymouse resource. 363 * @param include the dependency include 364 * @return the resource 365 * @exception IllegalArgumentException if the include mode is not URN mode 366 * @exception URISyntaxException if the include urn is invaid 367 */ 368 DefaultResource getAnonymousResource( String urn, Properties properties ) 369 throws URISyntaxException 370 { 371 if( m_anonymous.containsKey( urn ) ) 372 { 373 return (DefaultResource) m_anonymous.get( urn ); 374 } 375 376 Artifact artifact = Artifact.createArtifact( urn ); 377 String scheme = artifact.getScheme(); 378 String group = artifact.getGroup(); 379 String name = artifact.getName(); 380 String version = artifact.getVersion(); 381 String type = artifact.getType(); 382 383 ResourceDirective resourceDirective = 384 ResourceDirective.createAnonymousResource( scheme, name, version, type, properties ); 385 386 ModuleDirective enclosing = null; 387 String[] elements = group.split( "/", -1 ); 388 for( int i = ( elements.length-1 ); i>-1; i-- ) 389 { 390 String elem = elements[i]; 391 if( i == ( elements.length-1 ) ) 392 { 393 enclosing = new ModuleDirective( elem, version, resourceDirective ); 394 } 395 else 396 { 397 enclosing = new ModuleDirective( elem, version, enclosing ); 398 } 399 } 400 try 401 { 402 DefaultModule root = new DefaultModule( this, m_directive ); 403 root.addResource( enclosing ); 404 DefaultResource resource = root.getDefaultResource( group + "/" + name ); 405 m_anonymous.put( urn, resource ); 406 return resource; 407 } 408 catch( Exception e ) 409 { 410 final String error = 411 "Internal error while creating an ANONYMOUS resource: " 412 + urn 413 + "]."; 414 throw new RuntimeException( error, e ); 415 } 416 } 417 418 File getRootDirectory() 419 { 420 return m_root; 421 } 422 423 /** 424 * Return the array of top-level modules. 425 * @return the top-level module array 426 */ 427 DefaultModule[] getDefaultModules() 428 { 429 return m_module.getDefaultModules(); 430 } 431 432 /** 433 * Recursively lookup a resource using a fully qualified reference. 434 * @param ref the fully qualified resource name 435 * @return the resource instance 436 */ 437 DefaultResource getDefaultResource( String ref ) 438 { 439 return m_module.getDefaultResource( ref ); 440 } 441 442 /** 443 * Return a named module. 444 * @param ref the fully qualified resource name 445 * @return the module 446 */ 447 DefaultModule getDefaultModule( String ref ) 448 { 449 return m_module.getDefaultModule( ref ); 450 } 451 452 //---------------------------------------------------------------------------- 453 // selection 454 //---------------------------------------------------------------------------- 455 456 DefaultResource[] selectDefaultResources( String spec ) 457 { 458 return m_module.selectDefaultResources( spec ); 459 } 460 461 //---------------------------------------------------------------------------- 462 // other internals 463 //---------------------------------------------------------------------------- 464 465 private Logger getLogger() 466 { 467 return m_logger; 468 } 469 470 //---------------------------------------------------------------------------- 471 // static utilities 472 //---------------------------------------------------------------------------- 473 474 private static File resolveLibrarySource() throws FileNotFoundException 475 { 476 String path = System.getProperty( "user.dir" ); 477 File dir = new File( path ); 478 return resolveLibrarySource( dir ); 479 } 480 481 private static File resolveLibrarySource( File dir ) throws FileNotFoundException 482 { 483 if( dir.isFile() ) 484 { 485 throw new IllegalArgumentException( "not-a-directory" ); 486 } 487 else 488 { 489 File file = new File( dir, INDEX_FILENAME ); 490 if( file.isFile() && file.exists() ) 491 { 492 return file; 493 } 494 else 495 { 496 File parent = dir.getParentFile(); 497 if( parent != null ) 498 { 499 return resolveLibrarySource( parent ); 500 } 501 } 502 } 503 throw new FileNotFoundException( "index.xml" ); 504 } 505 }