001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.java.api.common.classpath;
043:
044: import java.io.File;
045: import java.net.URI;
046: import java.util.ArrayList;
047: import java.util.Arrays;
048: import java.util.HashSet;
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.Set;
052: import org.netbeans.api.project.ant.AntArtifact;
053: import org.netbeans.api.project.libraries.Library;
054: import org.netbeans.api.project.libraries.LibraryManager;
055: import org.netbeans.api.queries.CollocationQuery;
056: import org.netbeans.spi.project.support.ant.AntProjectHelper;
057: import org.netbeans.spi.project.support.ant.EditableProperties;
058: import org.netbeans.spi.project.support.ant.PropertyEvaluator;
059: import org.netbeans.spi.project.support.ant.PropertyUtils;
060: import org.netbeans.spi.project.support.ant.ReferenceHelper;
061: import org.openide.filesystems.FileUtil;
062: import org.openide.util.Parameters;
063:
064: /**
065: *
066: * @author Petr Hrebejk, Tomas Mysik
067: */
068: public final class ClassPathSupport {
069:
070: private final PropertyEvaluator evaluator;
071: private final ReferenceHelper referenceHelper;
072: private final AntProjectHelper antProjectHelper;
073: private final Set<String> wellKnownPaths;
074: private final String libraryPrefix;
075: private final String librarySuffix;
076: private final String antArtifactPrefix;
077:
078: public static ClassPathSupport create(PropertyEvaluator evaluator,
079: ReferenceHelper referenceHelper,
080: AntProjectHelper antProjectHelper, String[] wellKnownPaths,
081: String libraryPrefix, String librarySuffix,
082: String antArtifactPrefix) {
083: Parameters.notNull("evaluator", evaluator);
084: Parameters.notNull("referenceHelper", referenceHelper);
085: Parameters.notNull("antProjectHelper", antProjectHelper);
086: Parameters.notNull("libraryPrefix", libraryPrefix);
087: Parameters.notNull("librarySuffix", librarySuffix);
088: Parameters.notNull("antArtifactPrefix", antArtifactPrefix);
089:
090: return new ClassPathSupport(evaluator, referenceHelper,
091: antProjectHelper, wellKnownPaths, libraryPrefix,
092: librarySuffix, antArtifactPrefix);
093: }
094:
095: ClassPathSupport(PropertyEvaluator evaluator,
096: ReferenceHelper referenceHelper,
097: AntProjectHelper antProjectHelper, String[] wellKnownPaths,
098: String libraryPrefix, String librarySuffix,
099: String antArtifactPrefix) {
100: assert evaluator != null;
101: assert referenceHelper != null;
102: assert antProjectHelper != null;
103: assert libraryPrefix != null;
104: assert librarySuffix != null;
105: assert antArtifactPrefix != null;
106:
107: this .evaluator = evaluator;
108: this .referenceHelper = referenceHelper;
109: this .antProjectHelper = antProjectHelper;
110: if (wellKnownPaths == null) {
111: this .wellKnownPaths = null;
112: } else {
113: this .wellKnownPaths = new HashSet<String>(Arrays
114: .asList(wellKnownPaths));
115: }
116: this .libraryPrefix = libraryPrefix;
117: this .librarySuffix = librarySuffix;
118: this .antArtifactPrefix = antArtifactPrefix;
119: }
120:
121: /**
122: * Creates list of <CODE>Items</CODE> from given property.
123: */
124: public Iterator<Item> itemsIterator(String propertyValue) {
125: // XXX More performance frendly impl. would retrun a lazzy iterator.
126: return itemsList(propertyValue).iterator();
127: }
128:
129: public List<Item> itemsList(String propertyValue) {
130: String pe[] = PropertyUtils
131: .tokenizePath(propertyValue == null ? ""
132: : propertyValue); // NOI18N
133: List<Item> items = new ArrayList<Item>(pe.length);
134: for (String p : pe) {
135: Item item;
136:
137: // first try to find out whether the item is well known classpath
138: if (isWellKnownPath(p)) {
139: // some well know classpath
140: item = Item.create(p);
141: } else if (isLibrary(p)) {
142: // library from library manager
143: String libraryName = p.substring(
144: libraryPrefix.length(), p.lastIndexOf('.')); //NOI18N
145: Library library = LibraryManager.getDefault()
146: .getLibrary(libraryName);
147: if (library == null) {
148: item = Item.createBroken(
149: ClassPathItem.Type.LIBRARY, p);
150: } else {
151: item = Item.create(library, p);
152: }
153: } else if (isAntArtifact(p)) {
154: // ant artifact from another project
155: Object[] ret = referenceHelper
156: .findArtifactAndLocation(p);
157: if (ret[0] == null || ret[1] == null) {
158: item = Item.createBroken(
159: ClassPathItem.Type.ARTIFACT, p);
160: } else {
161: // fix of issue #55316
162: AntArtifact artifact = (AntArtifact) ret[0];
163: URI uri = (URI) ret[1];
164: File usedFile = antProjectHelper
165: .resolveFile(evaluator.evaluate(p));
166: File artifactFile = new File(artifact
167: .getScriptLocation().toURI().resolve(uri)
168: .normalize());
169: if (usedFile.equals(artifactFile)) {
170: item = Item.create(artifact, uri, p);
171: } else {
172: item = Item.createBroken(
173: ClassPathItem.Type.ARTIFACT, p);
174: }
175: }
176: } else {
177: // standalone jar or property
178: String eval = evaluator.evaluate(p);
179: File f = null;
180: if (eval != null) {
181: f = antProjectHelper.resolveFile(eval);
182: }
183: if (f == null || !f.exists()) {
184: item = Item.createBroken(f, p);
185: } else {
186: item = Item.create(f, p);
187: }
188: }
189: items.add(item);
190: }
191:
192: return items;
193:
194: }
195:
196: /**
197: * Converts list of classpath items into array of Strings.
198: * !! This method creates references in the project !!
199: */
200: public String[] encodeToStrings(final List<Item> items) {
201: List<String> result = new ArrayList<String>();
202: for (Item item : items) {
203: String reference = getReference(item);
204: if (reference != null) {
205: result.add(reference);
206: }
207: }
208:
209: // XXX create util method
210: String[] strings = new String[result.size()];
211: for (int i = 0; i < result.size(); i++) {
212: if (i < result.size() - 1) {
213: strings[i] = result.get(i) + ":"; // NOI18N
214: } else {
215: strings[i] = result.get(i);
216: }
217: }
218: return strings;
219: }
220:
221: public String getReference(final Item item) {
222: String reference = null;
223: switch (item.getType()) {
224: case JAR:
225: reference = item.getReference();
226: if (item.isBroken()) {
227: break;
228: }
229: if (reference == null) {
230: // new file
231: File file = item.getFile();
232: // pass null as expected artifact type to always get file reference
233: reference = referenceHelper.createForeignFileReference(
234: file, null);
235: }
236: break;
237: case LIBRARY:
238: reference = item.getReference();
239: if (item.isBroken()) {
240: break;
241: }
242: Library library = item.getLibrary();
243: if (reference == null) {
244: if (library == null) {
245: break;
246: }
247: reference = getLibraryReference(item);
248: }
249: break;
250: case ARTIFACT:
251: reference = item.getReference();
252: if (item.isBroken()) {
253: break;
254: }
255: AntArtifact artifact = item.getArtifact();
256: if (reference == null) {
257: if (artifact == null) {
258: break;
259: }
260: reference = referenceHelper.addReference(item
261: .getArtifact(), item.getArtifactURI());
262: }
263: break;
264: case CLASSPATH:
265: reference = item.getReference();
266: break;
267: }
268: return reference;
269: }
270:
271: public String getLibraryReference(Item item) {
272: if (item.getType() != ClassPathItem.Type.LIBRARY) {
273: throw new IllegalArgumentException(
274: "Item must be of type LIBRARY");
275: }
276: return libraryPrefix + item.getLibrary().getName()
277: + librarySuffix;
278: }
279:
280: // Private methods ---------------------------------------------------------
281:
282: private boolean isWellKnownPath(String property) {
283: return wellKnownPaths == null ? false : wellKnownPaths
284: .contains(property);
285: }
286:
287: private boolean isAntArtifact(String property) {
288: return antArtifactPrefix == null ? false : property
289: .startsWith(antArtifactPrefix);
290: }
291:
292: private boolean isLibrary(String property) {
293: if (libraryPrefix != null && property.startsWith(libraryPrefix)) {
294: return librarySuffix == null ? true : property
295: .endsWith(librarySuffix);
296: }
297: return false;
298: }
299:
300: // Innerclasses ------------------------------------------------------------
301:
302: /** Item of the classpath.
303: */
304: public static final class Item implements ClassPathItem {
305:
306: private final ClassPathItem.Type type;
307: private final Object object;
308: private final String property;
309: private final boolean broken;
310: private URI artifactURI;
311:
312: private Item(ClassPathItem.Type type, Object object,
313: String property, boolean broken) {
314: this .type = type;
315: this .object = object;
316: this .property = property;
317: this .broken = broken;
318: }
319:
320: private Item(ClassPathItem.Type type, Object object,
321: String property) {
322: this (type, object, property, false);
323: }
324:
325: private Item(ClassPathItem.Type type, Object object,
326: URI artifactURI, String property) {
327: this (type, object, property, false);
328: this .artifactURI = artifactURI;
329: }
330:
331: // Factory methods -----------------------------------------------------
332:
333: public static Item create(Library library, String property) {
334: if (library == null) {
335: throw new IllegalArgumentException(
336: "library must not be null");
337: }
338: return new Item(ClassPathItem.Type.LIBRARY, library,
339: property);
340: }
341:
342: public static Item create(AntArtifact artifact,
343: URI artifactURI, String property) {
344: if (artifactURI == null) {
345: throw new IllegalArgumentException(
346: "artifactURI must not be null");
347: }
348: if (artifact == null) {
349: throw new IllegalArgumentException(
350: "artifact must not be null");
351: }
352: return new Item(ClassPathItem.Type.ARTIFACT, artifact,
353: artifactURI, property);
354: }
355:
356: public static Item create(File file, String property) {
357: if (file == null) {
358: throw new IllegalArgumentException(
359: "file must not be null");
360: }
361: return new Item(ClassPathItem.Type.JAR, file, property);
362: }
363:
364: public static Item create(String property) {
365: if (property == null) {
366: throw new IllegalArgumentException(
367: "property must not be null");
368: }
369: return new Item(ClassPathItem.Type.CLASSPATH, null,
370: property);
371: }
372:
373: public static Item createBroken(ClassPathItem.Type type,
374: String property) {
375: if (property == null) {
376: throw new IllegalArgumentException(
377: "property must not be null in broken items");
378: }
379: return new Item(type, null, property, true);
380: }
381:
382: public static Item createBroken(final File file, String property) {
383: if (property == null) {
384: throw new IllegalArgumentException(
385: "property must not be null in broken items");
386: }
387: return new Item(ClassPathItem.Type.JAR, file, property,
388: true);
389: }
390:
391: // Instance methods ----------------------------------------------------
392:
393: public ClassPathItem.Type getType() {
394: return type;
395: }
396:
397: public Library getLibrary() {
398: if (getType() != ClassPathItem.Type.LIBRARY) {
399: throw new IllegalArgumentException(
400: "Item is not of required type - LIBRARY");
401: }
402: assert object instanceof Library : "Invalid object type: "
403: + object.getClass().getName() + " instance: "
404: + object.toString() + " expected type: Library";
405: return (Library) object;
406: }
407:
408: public File getFile() {
409: if (getType() != ClassPathItem.Type.JAR) {
410: throw new IllegalArgumentException(
411: "Item is not of required type - JAR");
412: }
413: return (File) object;
414: }
415:
416: public AntArtifact getArtifact() {
417: if (getType() != ClassPathItem.Type.ARTIFACT) {
418: throw new IllegalArgumentException(
419: "Item is not of required type - ARTIFACT");
420: }
421: return (AntArtifact) object;
422: }
423:
424: public URI getArtifactURI() {
425: if (getType() != ClassPathItem.Type.ARTIFACT) {
426: throw new IllegalArgumentException(
427: "Item is not of required type - ARTIFACT");
428: }
429: return artifactURI;
430: }
431:
432: public String getReference() {
433: return property;
434: }
435:
436: public boolean isBroken() {
437: return this .broken;
438: }
439:
440: @Override
441: public int hashCode() {
442: int hash = getType().ordinal();
443:
444: if (this .broken) {
445: return 42;
446: }
447:
448: switch (getType()) {
449: case ARTIFACT:
450: hash += getArtifact().getType().hashCode();
451: hash += getArtifact().getScriptLocation().hashCode();
452: hash += getArtifactURI().hashCode();
453: break;
454: case CLASSPATH:
455: hash += property.hashCode();
456: break;
457: default:
458: hash += object.hashCode();
459: }
460: return hash;
461: }
462:
463: @Override
464: public boolean equals(Object itemObject) {
465: if (!(itemObject instanceof Item)) {
466: return false;
467: }
468:
469: Item item = (Item) itemObject;
470: if (getType() != item.getType()) {
471: return false;
472: }
473: if (isBroken() != item.isBroken()) {
474: return false;
475: }
476: if (isBroken()) {
477: return getReference().equals(item.getReference());
478: }
479:
480: switch (getType()) {
481: case ARTIFACT:
482: if (getArtifact().getType() != item.getArtifact()
483: .getType()) {
484: return false;
485: }
486: if (!getArtifact().getScriptLocation().equals(
487: item.getArtifact().getScriptLocation())) {
488: return false;
489: }
490: if (!getArtifactURI().equals(item.getArtifactURI())) {
491: return false;
492: }
493: return true;
494: case CLASSPATH:
495: return property.equals(item.property);
496: }
497: return object.equals(item.object);
498: }
499: }
500:
501: /**
502: * Tokenize library classpath and try to relativize all the jars.
503: * @param ep the editable properties in which the result should be stored
504: * @param aph AntProjectHelper used to resolve files
505: * @param libCpProperty the library classpath property
506: */
507: public static boolean relativizeLibraryClassPath(
508: final EditableProperties ep, final AntProjectHelper aph,
509: final String libCpProperty) {
510: String value = PropertyUtils.getGlobalProperties().getProperty(
511: libCpProperty);
512: // bugfix #42852, check if the classpath property is set, otherwise return null
513: if (value == null) {
514: return false;
515: }
516: String[] paths = PropertyUtils.tokenizePath(value);
517: StringBuilder sb = new StringBuilder();
518: File projectDir = FileUtil.toFile(aph.getProjectDirectory());
519: for (int i = 0; i < paths.length; i++) {
520: File f = aph.resolveFile(paths[i]);
521: if (CollocationQuery.areCollocated(f, projectDir)) {
522: sb.append(PropertyUtils.relativizeFile(projectDir, f));
523: } else {
524: return false;
525: }
526: if (i + 1 < paths.length) {
527: sb.append(File.pathSeparatorChar);
528: }
529: }
530: if (sb.length() == 0) {
531: return false;
532: }
533: ep.setProperty(libCpProperty, sb.toString());
534: ep
535: .setComment(
536: libCpProperty,
537: new String[] {
538: // XXX this should be I18N! Not least because the English is wrong...
539: "# Property "
540: + libCpProperty
541: + " is set here just to make sharing of project simpler.",
542: "# The library definition has always preference over this property." },
543: false);
544: return true;
545: }
546: }
|