2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.j2ee.clientproject.classpath;
46
import java.util.ArrayList;
47
import java.util.Arrays;
48
import java.util.Collections;
49
import java.util.HashSet;
50
import java.util.Iterator;
51
import java.util.LinkedList;
52
import java.util.List;
54
import org.netbeans.api.project.ant.AntArtifact;
55
import org.netbeans.api.project.libraries.Library;
56
import org.netbeans.api.project.libraries.LibraryManager;
57
import org.netbeans.api.queries.CollocationQuery;
58
import org.netbeans.modules.j2ee.clientproject.AppClientProjectType;
59
import org.netbeans.modules.j2ee.clientproject.ui.customizer.AppClientProjectProperties;
60
import org.netbeans.spi.project.support.ant.AntProjectHelper;
61
import org.netbeans.spi.project.support.ant.EditableProperties;
62
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
63
import org.netbeans.spi.project.support.ant.PropertyUtils;
64
import org.netbeans.spi.project.support.ant.ReferenceHelper;
65
import org.openide.filesystems.FileUtil;
66
import org.w3c.dom.Document;
67
import org.w3c.dom.Element;
68
import org.w3c.dom.Node;
69
import org.w3c.dom.NodeList;
70
import org.w3c.dom.Text;
74
* @author Petr Hrebejk
76
public class ClassPathSupport {
78
public final static String ELEMENT_INCLUDED_LIBRARIES = "included-library"; // NOI18N
80
private static final String ATTR_FILES = "files"; //NOI18N
81
private static final String ATTR_DIRS = "dirs"; //NOI18N
83
private PropertyEvaluator evaluator;
84
private ReferenceHelper referenceHelper;
85
private AntProjectHelper antProjectHelper;
86
private Set<String> wellKnownPaths;
87
private String libraryPrefix;
88
private String librarySuffix;
89
private String antArtifactPrefix;
91
/** Creates a new instance of ClassPathSupport */
92
public ClassPathSupport( PropertyEvaluator evaluator,
93
ReferenceHelper referenceHelper,
94
AntProjectHelper antProjectHelper,
95
String wellKnownPaths[],
98
String antArtifactPrefix ) {
99
this.evaluator = evaluator;
100
this.referenceHelper = referenceHelper;
101
this.antProjectHelper = antProjectHelper;
102
this.wellKnownPaths = wellKnownPaths == null ? null : new HashSet<String>( Arrays.asList( wellKnownPaths ) );
103
this.libraryPrefix = libraryPrefix;
104
this.librarySuffix = librarySuffix;
105
this.antArtifactPrefix = antArtifactPrefix;
108
/** Creates list of <CODE>Items</CODE> from given property.
110
public Iterator<Item> itemsIterator( String propertyValue, String includedLibrariesElement ) {
111
// XXX More performance frendly impl. would retrun a lazzy iterator.
112
return itemsList( propertyValue, includedLibrariesElement ).iterator();
115
public List<Item> itemsList( String propertyValue, String includedLibrariesElement ) {
117
// Get the list of items which are included in deployment
118
List<String> includedItems = (includedLibrariesElement != null) ?
119
getIncludedLibraries( antProjectHelper, includedLibrariesElement ) :
120
Collections.<String>emptyList();
122
String pe[] = PropertyUtils.tokenizePath( propertyValue == null ? "": propertyValue ); // NOI18N
123
List<Item> items = new ArrayList<Item>( pe.length );
124
for( int i = 0; i < pe.length; i++ ) {
125
String property = getAntPropertyName( pe[i] );
128
// First try to find out whether the item is well known classpath
129
if ( isWellKnownPath( pe[i] ) ) {
130
// Some well know classpath
131
item = Item.create( pe[i], false );
133
else if ( isLibrary( pe[i] ) ) {
134
//Library from library manager
135
String libraryName = pe[i].substring( libraryPrefix.length(), pe[i].lastIndexOf('.') ); //NOI18N
136
Library library = LibraryManager.getDefault().getLibrary( libraryName );
137
if ( library == null ) {
138
item = Item.createBroken( Item.TYPE_LIBRARY, pe[i], includedItems.contains( property ) );
141
item = Item.create( library, pe[i], includedItems.contains( property ) );
144
else if ( isAntArtifact( pe[i] ) ) {
145
// Ant artifact from another project
146
Object[] ret = referenceHelper.findArtifactAndLocation(pe[i]);
147
if ( ret[0] == null || ret[1] == null ) {
148
item = Item.createBroken( Item.TYPE_ARTIFACT, pe[i], includedItems.contains ( property ) );
151
//fix of issue #55391
152
AntArtifact artifact = (AntArtifact)ret[0];
153
URI uri = (URI)ret[1];
154
File usedFile = antProjectHelper.resolveFile(evaluator.evaluate(pe[i]));
155
File artifactFile = new File (artifact.getScriptLocation().toURI().resolve(uri).normalize());
156
if (usedFile.equals(artifactFile)) {
157
item = Item.create( artifact, uri, pe[i], includedItems.contains ( property ) );
160
item = Item.createBroken( Item.TYPE_ARTIFACT, pe[i], includedItems.contains ( property ) );
164
// Standalone jar or property
165
String eval = evaluator.evaluate( pe[i] );
168
f = antProjectHelper.resolveFile( eval );
171
if ( f == null || !f.exists() ) {
172
item = Item.createBroken( f, pe[i], includedItems.contains ( property ) );
175
item = Item.create( f, pe[i], includedItems.contains ( property ) );
187
/** Converts list of classpath items into array of Strings.
188
* !! This method creates references in the project !!
189
* !! This method may add <included-library> items to project.xml !!
191
public String[] encodeToStrings( Iterator /*<Item>*/ classpath, String includedLibrariesElement ) {
193
List<String> result = new ArrayList<String>();
194
List<String> includedLibraries = new ArrayList<String>();
196
List<Item> cp = new LinkedList<Item>();
198
while( classpath.hasNext() ) {
200
Item item = (Item)classpath.next();
202
String reference = null;
204
switch( item.getType() ) {
207
reference = item.getReference();
208
if ( item.isBroken() ) {
211
if (reference == null) {
213
File file = item.getFile();
214
// pass null as expected artifact type to always get file reference
215
reference = referenceHelper.createForeignFileReference(file, null);
216
item.setReference(reference);
219
case Item.TYPE_LIBRARY:
220
reference = item.getReference();
221
if ( item.isBroken() ) {
224
Library library = item.getLibrary();
225
if (reference == null) {
226
if ( library == null ) {
229
reference = getLibraryReference( item );
230
item.setReference(reference);
233
case Item.TYPE_ARTIFACT:
234
reference = item.getReference();
235
if ( item.isBroken() ) {
238
AntArtifact artifact = item.getArtifact();
239
if ( reference == null) {
240
if ( artifact == null ) {
243
reference = referenceHelper.addReference( item.getArtifact(), item.getArtifactURI());
244
item.setReference(reference);
247
case Item.TYPE_CLASSPATH:
248
reference = item.getReference();
252
if ( reference != null ) {
253
result.add( reference );
255
// Add the item to the list of items included in deployment
256
if ( includedLibrariesElement != null && item.isIncludedInDeployment() ) {
257
includedLibraries.add( getAntPropertyName( reference ) );
263
if ( includedLibrariesElement != null ) {
264
putIncludedLibraries( includedLibraries, cp, antProjectHelper, includedLibrariesElement );
267
String[] items = new String[ result.size() ];
268
for( int i = 0; i < result.size(); i++) {
269
if ( i < result.size() - 1 ) {
270
items[i] = result.get( i ) + ":"; // NOI18N
273
items[i] = result.get( i ); //NOI18N
280
public String getLibraryReference( Item item ) {
281
if ( item.getType() != Item.TYPE_LIBRARY ) {
282
throw new IllegalArgumentException( "Item must be of type LIBRARY" ); // NOI18N
284
return libraryPrefix + item.getLibrary().getName() + librarySuffix;
287
// Private methods ---------------------------------------------------------
289
private boolean isWellKnownPath( String property ) {
290
return wellKnownPaths == null ? false : wellKnownPaths.contains( property );
293
private boolean isAntArtifact( String property ) {
294
return antArtifactPrefix == null ? false : property.startsWith( antArtifactPrefix );
297
private boolean isLibrary( String property ) {
298
if ( libraryPrefix != null && property.startsWith( libraryPrefix ) ) {
299
return librarySuffix == null ? true : property.endsWith( librarySuffix );
307
// Private static methods --------------------------------------------------
310
* Returns a list with the classpath items which are to be included
313
private static List<String> getIncludedLibraries( AntProjectHelper antProjectHelper, String includedLibrariesElement ) {
314
assert antProjectHelper != null;
315
assert includedLibrariesElement != null;
317
Element data = antProjectHelper.getPrimaryConfigurationData( true );
318
NodeList libs = data.getElementsByTagNameNS( AppClientProjectType.PROJECT_CONFIGURATION_NAMESPACE, includedLibrariesElement );
319
List<String> libraries = new ArrayList<String>(libs.getLength());
320
for ( int i = 0; i < libs.getLength(); i++ ) {
321
Element item = (Element)libs.item( i );
322
libraries.add( findText( item ));
328
* Updates the project helper with the list of classpath items which are to be
329
* included in deployment.
331
private static void putIncludedLibraries( List<String> libraries, List<Item> classpath,
332
AntProjectHelper antProjectHelper, String includedLibrariesElement ) {
333
assert libraries != null;
334
assert antProjectHelper != null;
335
assert includedLibrariesElement != null;
337
Element data = antProjectHelper.getPrimaryConfigurationData( true );
338
NodeList libs = data.getElementsByTagNameNS( AppClientProjectType.PROJECT_CONFIGURATION_NAMESPACE, includedLibrariesElement );
339
while ( libs.getLength() > 0 ) {
340
Node n = libs.item( 0 );
341
n.getParentNode().removeChild( n );
344
Document doc = data.getOwnerDocument();
345
for (String libraryName : libraries) {
346
//find a correcponding classpath item for the library
347
for (ClassPathSupport.Item item : classpath) {
348
String libraryPropName = "${" + libraryName + "}"; // NOI18N
349
if(libraryPropName.equals(item.getReference())) {
350
data.appendChild(createLibraryElement(doc, libraryName, item, includedLibrariesElement));
355
antProjectHelper.putPrimaryConfigurationData( data, true );
358
private static Element createLibraryElement(Document doc, String pathItem, Item item, String includedLibrariesElement ) {
359
Element libraryElement = doc.createElementNS( AppClientProjectType.PROJECT_CONFIGURATION_NAMESPACE, includedLibrariesElement );
360
List<File> files = new ArrayList<File>();
361
List<File> dirs = new ArrayList<File>();
362
AppClientProjectProperties.getFilesForItem(item, files, dirs);
363
if (files.size() > 0) {
364
libraryElement.setAttribute(ATTR_FILES, "" + files.size());
366
if (dirs.size() > 0) {
367
libraryElement.setAttribute(ATTR_DIRS, "" + dirs.size());
370
libraryElement.appendChild( doc.createTextNode( pathItem ) );
371
return libraryElement;
375
* Extracts <b>the first</b> nested text from an element.
376
* Currently does not handle coalescing text nodes, CDATA sections, etc.
377
* @param parent a parent element
378
* @return the nested text, or null if none was found
380
private static String findText( Element parent ) {
381
NodeList l = parent.getChildNodes();
382
for ( int i = 0; i < l.getLength(); i++ ) {
383
if ( l.item(i).getNodeType() == Node.TEXT_NODE ) {
384
Text text = (Text)l.item( i );
385
return text.getNodeValue();
391
// Innerclasses ------------------------------------------------------------
393
/** Item of the classpath.
395
public static class Item {
397
// Types of the classpath elements
398
public static final int TYPE_JAR = 0;
399
public static final int TYPE_LIBRARY = 1;
400
public static final int TYPE_ARTIFACT = 2;
401
public static final int TYPE_CLASSPATH = 3;
403
private Object object;
404
private URI artifactURI;
406
private String property;
407
private boolean includedInDeployment;
409
private final boolean broken;
411
private Item(int type, Object object, String property, boolean included, String raw, boolean broken) {
413
this.object = object;
414
this.property = property;
415
this.includedInDeployment = included;
417
this.broken = broken;
420
private Item( int type, Object object, URI artifactURI, String property, boolean included ) {
421
this(type, object, property, included, null, false);
422
this.artifactURI = artifactURI;
425
private Item(int type, Object object, String property, boolean included, String raw) {
426
this(type, object, property, included, null, false);
429
// Factory methods -----------------------------------------------------
432
public static Item create( Library library, String property, boolean included ) {
433
if ( library == null ) {
434
throw new IllegalArgumentException( "library must not be null" ); // NOI18N
436
String libraryName = library.getName();
437
return new Item( TYPE_LIBRARY, library, property, included, AppClientProjectProperties.LIBRARY_PREFIX + libraryName + AppClientProjectProperties.LIBRARY_SUFFIX);
440
public static Item create( AntArtifact artifact, URI artifactURI, String property, boolean included ) {
441
if ( artifactURI == null ) {
442
throw new IllegalArgumentException( "artifactURI must not be null" ); // NOI18N
444
if ( artifact == null ) {
445
throw new IllegalArgumentException( "artifact must not be null" ); // NOI18N
447
return new Item( TYPE_ARTIFACT, artifact, artifactURI, property, included );
450
public static Item create( File file, String property, boolean included ) {
451
if ( file == null ) {
452
throw new IllegalArgumentException( "file must not be null" ); // NOI18N
454
return new Item( TYPE_JAR, file, property, included, null );
457
public static Item create( String property, boolean included ) {
458
if ( property == null ) {
459
throw new IllegalArgumentException( "property must not be null" ); // NOI18N
461
return new Item ( TYPE_CLASSPATH, null, property, included, null );
464
public static Item createBroken( int type, String property, boolean included ) {
465
if ( property == null ) {
466
throw new IllegalArgumentException( "property must not be null in broken items" ); // NOI18N
468
return new Item(type, null, property, included, null, true);
471
public static Item createBroken(File file, String property, boolean included) {
472
return new Item(TYPE_JAR, file, property, included, null, true);
475
// Instance methods ----------------------------------------------------
477
public String getRaw() {
481
public int getType() {
485
public Library getLibrary() {
486
if ( getType() != TYPE_LIBRARY ) {
487
throw new IllegalArgumentException( "Item is not of required type - LIBRARY" ); // NOI18N
492
return (Library)object;
495
public File getFile() {
496
if ( getType() != TYPE_JAR ) {
497
throw new IllegalArgumentException( "Item is not of required type - JAR" ); // NOI18N
499
// for broken item: one will get java.io.File or null (#113390)
503
public AntArtifact getArtifact() {
504
if ( getType() != TYPE_ARTIFACT ) {
505
throw new IllegalArgumentException( "Item is not of required type - ARTIFACT" ); // NOI18N
510
return (AntArtifact)object;
513
public URI getArtifactURI() {
514
if ( getType() != TYPE_ARTIFACT ) {
515
throw new IllegalArgumentException( "Item is not of required type - ARTIFACT" ); // NOI18N
521
public String getReference() {
525
public void setReference(String property) {
526
this.property = property;
529
public boolean isIncludedInDeployment() {
530
// boolean result = includedInDeployment;
531
// if (getType() == TYPE_JAR) {
532
// at the moment we can't include folders in deployment
533
// FileObject fo = FileUtil.toFileObject(getFile());
534
// if (fo == null || fo.isFolder())
537
return includedInDeployment;
540
public void setIncludedInDeployment(boolean includedInDeployment) {
541
this.includedInDeployment = includedInDeployment;
544
public boolean isBroken() {
549
public String toString() {
550
return "artifactURI=" + artifactURI // NOI18N
551
+ ", type=" + type // NOI18N
552
+ ", property=" + property // NOI18N
553
+ ", includedInDeployment=" + includedInDeployment // NOI18N
554
+ ", raw=" + raw // NOI18N
555
+ ", object=" + object // NOI18N
556
+ ", broken=" + broken; // NOI18N
560
public int hashCode() {
562
int hash = getType();
568
switch ( getType() ) {
570
hash += getArtifact().getType().hashCode();
571
hash += getArtifact().getScriptLocation().hashCode();
572
hash += getArtifactURI().hashCode();
575
hash += property.hashCode();
578
hash += object.hashCode();
585
public boolean equals( Object itemObject ) {
587
if ( !( itemObject instanceof Item ) ) {
591
Item item = (Item)itemObject;
593
if ( getType() != item.getType() ) {
597
if ( isBroken() != item.isBroken() ) {
602
return getReference().equals( item.getReference() );
605
switch ( getType() ) {
607
if ( getArtifact().getType() != item.getArtifact().getType() ) {
611
if ( !getArtifact().getScriptLocation().equals( item.getArtifact().getScriptLocation() ) ) {
615
if ( !getArtifactURI().equals( item.getArtifactURI() ) ) {
620
return property.equals( item.property );
622
return this.object.equals( item.object );
632
* Tokenize library classpath and try to relativize all the jars.
633
* @param ep the editable properties in which the result should be stored
634
* @param aph AntProjectHelper used to resolve files
635
* @param libCpProperty the library classpath property
637
public static boolean relativizeLibraryClassPath (final EditableProperties ep, final AntProjectHelper aph, final String libCpProperty) {
638
String value = PropertyUtils.getGlobalProperties().getProperty(libCpProperty);
639
// bugfix #42852, check if the classpath property is set, otherwise return null
643
String[] paths = PropertyUtils.tokenizePath(value);
644
StringBuffer sb = new StringBuffer();
645
File projectDir = FileUtil.toFile(aph.getProjectDirectory());
646
for (int i=0; i<paths.length; i++) {
647
File f = aph.resolveFile(paths[i]);
648
if (CollocationQuery.areCollocated(f, projectDir)) {
649
sb.append(PropertyUtils.relativizeFile(projectDir, f));
653
if (i+1<paths.length) {
654
sb.append(File.pathSeparatorChar);
657
if (sb.length() == 0) {
660
ep.setProperty(libCpProperty, sb.toString());
661
ep.setComment(libCpProperty, new String[]{
662
// XXX this should be I18N! Not least because the English is wrong...
663
"# Property "+libCpProperty+" is set here just to make sharing of project simpler.", // NOI18N
664
"# The library definition has always preference over this property."}, false); // NOI18N
669
* Converts the ant reference to the name of the referenced property
670
* @param ant reference
671
* @param the name of the referenced property
673
public static String getAntPropertyName( String property ) {
674
if ( property != null &&
675
property.startsWith( "${" ) && // NOI18N
676
property.endsWith( "}" ) ) { // NOI18N
677
return property.substring( 2, property.length() - 1 );