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.io.IOException;
046: import java.net.URI;
047: import java.net.URL;
048: import java.util.ArrayList;
049: import java.util.Iterator;
050: import java.util.List;
051: import org.netbeans.api.java.classpath.ClassPath;
052: import org.netbeans.api.java.project.JavaProjectConstants;
053: import org.netbeans.api.project.Project;
054: import org.netbeans.api.project.ProjectManager;
055: import org.netbeans.api.project.SourceGroup;
056: import org.netbeans.api.project.Sources;
057: import org.netbeans.api.project.ant.AntArtifact;
058: import org.netbeans.api.project.libraries.Library;
059: import org.netbeans.modules.java.api.common.SourceRoots;
060: import org.netbeans.modules.java.api.common.ant.UpdateHelper;
061: import org.netbeans.spi.project.support.ant.AntProjectHelper;
062: import org.netbeans.spi.project.support.ant.EditableProperties;
063: import org.netbeans.spi.project.support.ant.PropertyEvaluator;
064: import org.netbeans.spi.project.support.ant.ReferenceHelper;
065: import org.openide.filesystems.FileObject;
066: import org.openide.filesystems.FileUtil;
067: import org.openide.util.Mutex;
068: import org.openide.util.MutexException;
069: import org.openide.util.Parameters;
070:
071: /**
072: * @author Tomas Zezula, Tomas Mysik
073: */
074: public final class ProjectClassPathModifierSupport<T extends ClassPathItem> {
075:
076: public static enum Operation {
077: ADD, REMOVE
078: }
079:
080: private final Project project;
081: private final UpdateHelper helper;
082: private final ReferenceHelper refHelper;
083: private final PropertyEvaluator eval;
084: private final SourceRoots sourceRoots;
085: private final SourceRoots testSourceRoots;
086: private final Properties properties;
087:
088: // XXX javadoc
089: public static <T extends ClassPathItem> ProjectClassPathModifierSupport<T> create(
090: Project project, UpdateHelper helper,
091: PropertyEvaluator eval, ReferenceHelper refHelper,
092: SourceRoots sourceRoots, SourceRoots testSourceRoots,
093: Properties properties) {
094:
095: return new ProjectClassPathModifierSupport<T>(project, helper,
096: eval, refHelper, sourceRoots, testSourceRoots,
097: properties);
098: }
099:
100: private ProjectClassPathModifierSupport(Project project,
101: UpdateHelper helper, PropertyEvaluator eval,
102: ReferenceHelper refHelper, SourceRoots sourceRoots,
103: SourceRoots testSourceRoots, Properties properties) {
104: Parameters.notNull("project", project);
105: Parameters.notNull("helper", helper);
106: Parameters.notNull("eval", eval);
107: Parameters.notNull("refHelper", refHelper);
108: Parameters.notNull("sourceRoots", sourceRoots);
109: Parameters.notNull("testSourceRoots", testSourceRoots);
110: Parameters.notNull("properties", properties);
111:
112: this .project = project;
113: this .helper = helper;
114: this .eval = eval;
115: this .refHelper = refHelper;
116: this .sourceRoots = sourceRoots;
117: this .testSourceRoots = testSourceRoots;
118: this .properties = properties;
119: }
120:
121: public SourceGroup[] getExtensibleSourceGroups() {
122: Sources s = project.getLookup().lookup(Sources.class);
123: assert s != null;
124: return s
125: .getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
126: }
127:
128: public String[] getExtensibleClassPathTypes(SourceGroup sg) {
129: return new String[] { ClassPath.COMPILE, ClassPath.EXECUTE };
130: }
131:
132: public boolean handleRoots(final URL[] classPathRoots,
133: final String classPathProperty, final Operation operation,
134: final ClassPathItemProvider<T> provider)
135: throws IOException, UnsupportedOperationException {
136: assert classPathRoots != null : "The classPathRoots cannot be null";
137: assert classPathProperty != null;
138:
139: try {
140: return ProjectManager.mutex().writeAccess(
141: new Mutex.ExceptionAction<Boolean>() {
142: public Boolean run() throws Exception {
143: EditableProperties props = helper
144: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
145: String raw = props
146: .getProperty(classPathProperty);
147: List<T> resources = provider
148: .getClassPathItems(raw);
149: boolean changed = false;
150: for (URL classPathRoot : classPathRoots) {
151: assert classPathRoot != null;
152: assert classPathRoot.toExternalForm()
153: .endsWith("/");
154:
155: URL toAdd = FileUtil
156: .getArchiveFile(classPathRoot);
157: if (toAdd == null) {
158: toAdd = classPathRoot;
159: }
160: File f = FileUtil
161: .normalizeFile(new File(
162: URI
163: .create(toAdd
164: .toExternalForm())));
165: if (f == null) {
166: throw new IllegalArgumentException(
167: "The file must exist on disk");
168: }
169: T item = provider.createClassPathItem(
170: f, null);
171: if (operation == Operation.ADD
172: && !resources.contains(item)) {
173: resources.add(item);
174: changed = true;
175: } else if (operation == Operation.REMOVE) {
176: if (resources.remove(item)) {
177: changed = true;
178: } else {
179: for (Iterator<T> it = resources
180: .iterator(); it
181: .hasNext();) {
182: T itm = it.next();
183: if (itm.isBroken()
184: && itm.getType() == ClassPathItem.Type.JAR
185: && f.equals(itm
186: .getFile())) {
187: it.remove();
188: changed = true;
189: }
190: }
191: }
192: }
193: }
194: if (changed) {
195: String[] itemRefs = provider
196: .encodeToStrings(resources);
197: // PathParser may change the EditableProperties
198: props = helper
199: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
200: props.setProperty(classPathProperty,
201: itemRefs);
202: helper
203: .putProperties(
204: AntProjectHelper.PROJECT_PROPERTIES_PATH,
205: props);
206: ProjectManager.getDefault()
207: .saveProject(project);
208: return true;
209: }
210: return false;
211: }
212: });
213: } catch (Exception e) {
214: if (e instanceof IOException) {
215: throw (IOException) e;
216: } else {
217: IOException t = new IOException();
218: t.initCause(e);
219: throw t;
220: }
221: }
222: }
223:
224: public boolean handleAntArtifacts(final AntArtifact[] artifacts,
225: final URI[] artifactElements,
226: final String classPathProperty, final Operation operation,
227: final ClassPathItemProvider<T> provider)
228: throws IOException, UnsupportedOperationException {
229: assert artifacts != null : "Artifacts cannot be null";
230: assert artifactElements != null : "ArtifactElements cannot be null";
231: assert artifacts.length == artifactElements.length : "Each artifact has to have corresponding artifactElement";
232: assert classPathProperty != null;
233:
234: try {
235: return ProjectManager.mutex().writeAccess(
236: new Mutex.ExceptionAction<Boolean>() {
237: public Boolean run() throws Exception {
238: EditableProperties props = helper
239: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
240: String raw = props
241: .getProperty(classPathProperty);
242: List<T> resources = provider
243: .getClassPathItems(raw);
244: boolean changed = false;
245: for (int i = 0; i < artifacts.length; i++) {
246: assert artifacts[i] != null;
247: assert artifactElements[i] != null;
248:
249: T item = provider.createClassPathItem(
250: artifacts[i],
251: artifactElements[i], null);
252: if (operation == Operation.ADD
253: && !resources.contains(item)) {
254: resources.add(item);
255: changed = true;
256: } else if (operation == Operation.REMOVE
257: && resources.contains(item)) {
258: resources.remove(item);
259: changed = true;
260: }
261: }
262: if (changed) {
263: String[] itemRefs = provider
264: .encodeToStrings(resources);
265: // reread the properties, PathParser changes them
266: props = helper
267: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
268: props.setProperty(classPathProperty,
269: itemRefs);
270: helper
271: .putProperties(
272: AntProjectHelper.PROJECT_PROPERTIES_PATH,
273: props);
274: ProjectManager.getDefault()
275: .saveProject(project);
276: return true;
277: }
278: return false;
279: }
280: });
281: } catch (Exception e) {
282: if (e instanceof IOException) {
283: throw (IOException) e;
284: } else {
285: IOException t = new IOException();
286: t.initCause(e);
287: throw t;
288: }
289: }
290: }
291:
292: public boolean handleLibraries(final Library[] libraries,
293: final String classPathProperty, final Operation operation,
294: final ClassPathItemProvider<T> provider)
295: throws IOException, UnsupportedOperationException {
296: assert libraries != null : "Libraries cannot be null";
297: assert classPathProperty != null;
298:
299: try {
300: return ProjectManager.mutex().writeAccess(
301: new Mutex.ExceptionAction<Boolean>() {
302: public Boolean run() throws IOException {
303: EditableProperties props = helper
304: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
305: String raw = props
306: .getProperty(classPathProperty);
307: List<T> resources = provider
308: .getClassPathItems(raw);
309: List<T> changed = new ArrayList<T>(
310: libraries.length);
311: for (Library library : libraries) {
312: assert library != null;
313:
314: T item = provider.createClassPathItem(
315: library, null);
316: if (operation == Operation.ADD
317: && !resources.contains(item)) {
318: resources.add(item);
319: changed.add(item);
320: } else if (operation == Operation.REMOVE
321: && resources.contains(item)) {
322: resources.remove(item);
323: changed.add(item);
324: }
325: }
326: if (!changed.isEmpty()) {
327: String[] itemRefs = provider
328: .encodeToStrings(resources);
329: // PathParser may change the EditableProperties
330: props = helper
331: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
332: props.setProperty(classPathProperty,
333: itemRefs);
334: if (operation == Operation.ADD) {
335: for (T item : changed) {
336: String prop = provider
337: .getLibraryReference(item);
338: // XXX make a PropertyUtils method for this!
339: prop = prop.substring(2, prop
340: .length() - 1);
341: ClassPathSupport
342: .relativizeLibraryClassPath(
343: props,
344: helper
345: .getAntProjectHelper(),
346: prop);
347: }
348: }
349: helper
350: .putProperties(
351: AntProjectHelper.PROJECT_PROPERTIES_PATH,
352: props);
353: ProjectManager.getDefault()
354: .saveProject(project);
355: return true;
356: }
357: return false;
358: }
359: });
360: } catch (MutexException e) {
361: throw (IOException) e.getException();
362: }
363: }
364:
365: public String getClassPathProperty(final SourceGroup sg,
366: final String type) throws UnsupportedOperationException {
367: assert sg != null : "SourceGroup cannot be null";
368: assert type != null : "Type cannot be null";
369:
370: final String classPathProperty = getPropertyName(sg, type);
371: if (classPathProperty == null) {
372: throw new UnsupportedOperationException("Modification of ["
373: + sg.getRootFolder().getPath() + ", " + type
374: + "] is not supported");
375: }
376: return classPathProperty;
377: }
378:
379: // XXX used to be in ClassPathProviderImpl
380: /**
381: * Get the name of the property of the classpath for the given source group and classpath type.
382: * The property is searched in sources as well as tests and can be <code>null</code> if the root of the given
383: * source group is not found.
384: * @param sg source group the property is searched for.
385: * @param type classpath type - compile or execute, see {@link ClassPath} for more information.
386: * @return the property name or <code>null</code> if nothing found.
387: */
388: private String getPropertyName(SourceGroup sg, String type) {
389: FileObject root = sg.getRootFolder();
390: for (FileObject fo : sourceRoots.getRoots()) {
391: if (root.equals(fo)) {
392: if (ClassPath.COMPILE.equals(type)) {
393: return properties.sourceCompileTimeClassPath;
394: } else if (ClassPath.EXECUTE.equals(type)) {
395: return properties.sourceRunTimeClassPath;
396: }
397: return null;
398: }
399: }
400: for (FileObject fo : testSourceRoots.getRoots()) {
401: if (root.equals(fo)) {
402: if (ClassPath.COMPILE.equals(type)) {
403: return properties.testSourceCompileTimeClassPath;
404: } else if (ClassPath.EXECUTE.equals(type)) {
405: return properties.testSourceRunTimeClassPath;
406: }
407: return null;
408: }
409: }
410: return null;
411: }
412:
413: /**
414: * Class holding different properties like "javac.classpath" etc.
415: */
416: public static final class Properties {
417: final String sourceCompileTimeClassPath;
418: final String sourceRunTimeClassPath;
419: final String testSourceCompileTimeClassPath;
420: final String testSourceRunTimeClassPath;
421:
422: public Properties(String sourceCompileTimeClassPath,
423: String sourceRunTimeClassPath,
424: String testSourceCompileTimeClassPath,
425: String testSourceRunTimeClassPath) {
426: Parameters.notNull("sourceCompileTimeClassPath",
427: sourceCompileTimeClassPath);
428: Parameters.notNull("sourceRunTimeClassPath",
429: sourceRunTimeClassPath);
430: Parameters.notNull("testSourceCompileTimeClassPath",
431: testSourceCompileTimeClassPath);
432: Parameters.notNull("testSourceRunTimeClassPath",
433: testSourceRunTimeClassPath);
434:
435: this .sourceCompileTimeClassPath = sourceCompileTimeClassPath;
436: this .sourceRunTimeClassPath = sourceRunTimeClassPath;
437: this .testSourceCompileTimeClassPath = testSourceCompileTimeClassPath;
438: this .testSourceRunTimeClassPath = testSourceRunTimeClassPath;
439: }
440: }
441:
442: // XXX javadoc
443: public interface ClassPathItemProvider<T> {
444: T createClassPathItem(File file, String property);
445:
446: T createClassPathItem(AntArtifact antArtifact,
447: URI antArtifactURI, String property);
448:
449: T createClassPathItem(Library library, String property);
450:
451: List<T> getClassPathItems(String reference);
452:
453: String[] encodeToStrings(List<T> items);
454:
455: String getLibraryReference(T item);
456: }
457: }
|