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-2006 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.spi.java.project.classpath;
043:
044: import java.io.File;
045: import java.io.IOException;
046: import java.net.URI;
047: import java.net.URISyntaxException;
048: import java.net.URL;
049: import org.netbeans.api.project.SourceGroup;
050: import org.netbeans.api.project.ant.AntArtifact;
051: import org.netbeans.api.project.libraries.Library;
052: import org.netbeans.api.queries.CollocationQuery;
053: import org.netbeans.modules.java.project.classpath.ProjectClassPathModifierAccessor;
054: import org.netbeans.spi.project.libraries.support.LibrariesSupport;
055: import org.netbeans.spi.project.support.ant.AntProjectHelper;
056: import org.netbeans.spi.project.support.ant.PropertyUtils;
057: import org.openide.filesystems.FileObject;
058: import org.openide.filesystems.FileUtil;
059:
060: /**
061: * An SPI for project's classpaths modification.
062: * A project can provide subclass of this class in its {@link org.netbeans.api.project.Project#getLookup lookup} to
063: * allow clients to add or remove new classpath elements (JAR, folder, dependent project, or library) to its
064: * classpaths.
065: * @since org.netbeans.modules.java.project/1 1.10
066: */
067: public abstract class ProjectClassPathModifierImplementation {
068:
069: static {
070: ProjectClassPathModifierAccessor.INSTANCE = new Accessor();
071: }
072:
073: protected ProjectClassPathModifierImplementation() {
074: }
075:
076: /**
077: * Returns the {@link SourceGroup}s providing classpath(s)
078: * which may be modified.
079: * @return (possibly empty) array of {@link SourceGroup}, never returns null
080: */
081: protected abstract SourceGroup[] getExtensibleSourceGroups();
082:
083: /**
084: * Returns the types of classpaths for given {@link SourceGroup} which may be modified.
085: * @param sourceGroup for which the classpath types should be returned
086: * @return (possibly empty) array of classpath types, never returns null
087: */
088: protected abstract String[] getExtensibleClassPathTypes(
089: SourceGroup sourceGroup);
090:
091: /**
092: * Adds libraries into the project's classpath if the
093: * libraries are not already included.
094: * @param libraries to be added
095: * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA}
096: * identifying the compilation unit to change
097: * @param type the type of the classpath the library should be added to,
098: * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE}
099: * @return true in case the classpath was changed (at least one library was added to the classpath),
100: * the value false is returned when all the libraries are already included on the classpath.
101: * @exception IOException in case the project metadata cannot be changed
102: * @exception UnsupportedOperationException is thrown when the project does not support
103: * adding of a library to the classpath of the given type.
104: */
105: protected abstract boolean addLibraries(Library[] libraries,
106: SourceGroup sourceGroup, String type) throws IOException,
107: UnsupportedOperationException;
108:
109: /**
110: * Removes libraries from the project's classpath if the
111: * libraries are included on it.
112: * @param libraries to be removed
113: * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA}
114: * identifying the compilation unit to change
115: * @param type the type of the classpath the library should be removed from,
116: * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE}
117: * @return true in case the classpath was changed, (at least one library was removed from the classpath),
118: * the value false is returned when none of the libraries was included on the classpath.
119: * @exception IOException in case the project metadata cannot be changed
120: * @exception UnsupportedOperationException is thrown when the project does not support
121: * removing of a library from the classpath of the given type.
122: */
123: protected abstract boolean removeLibraries(Library[] libraries,
124: SourceGroup sourceGroup, String type) throws IOException,
125: UnsupportedOperationException;
126:
127: /**
128: * Adds archive files or folders into the project's classpath if the
129: * entries are not already there.
130: * @param classPathRoots roots to be added, each root has to be either a root of an archive or a folder
131: * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA}
132: * identifying the compilation unit to change
133: * @param type the type of the classpath the root should be added to,
134: * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE}
135: * @return true in case the classpath was changed, (at least one classpath root was added to the classpath),
136: * the value false is returned when all the classpath roots are already included on the classpath.
137: * @exception IOException in case the project metadata cannot be changed
138: * @exception UnsupportedOperationException is thrown when the project does not support
139: * adding of a root to the classpath of the given type.
140: */
141: protected abstract boolean addRoots(URL[] classPathRoots,
142: SourceGroup sourceGroup, String type) throws IOException,
143: UnsupportedOperationException;
144:
145: /**
146: * Removes archive files or folders from the project's classpath if the
147: * entries are included on it.
148: * @param classPathRoots roots to be removed, each root has to be either a root of an archive or a folder
149: * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA}
150: * identifying the compilation unit to change
151: * @param type the type of the classpath the root should be removed from,
152: * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE}
153: * @return true in case the classpath was changed, (at least one classpath root was removed from the classpath),
154: * the value false is returned when none of the classpath roots was included on the classpath.
155: * @exception IOException in case the project metadata cannot be changed
156: * @exception UnsupportedOperationException is thrown when the project does not support
157: * removing of a root from the classpath of the given type.
158: */
159: protected abstract boolean removeRoots(URL[] classPathRoots,
160: SourceGroup sourceGroup, String type) throws IOException,
161: UnsupportedOperationException;
162:
163: /**
164: * Adds artifacts (e.g. subprojects) into project's classpath if the
165: * artifacts are not already on it.
166: * @param artifacts to be added
167: * @param artifactElements the URIs of the build output, the artifactElements has to have the same length
168: * as artifacts.
169: * (must be owned by the artifact and be relative to it)
170: * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA}
171: * identifying the compilation unit to change
172: * @param type the type of the classpath the artifact should be added to,
173: * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE}
174: * @return true in case the classpath was changed, (at least one artifact was added to the classpath),
175: * the value false is returned when all the artifacts are already included on the classpath.
176: * @exception IOException in case the project metadata cannot be changed
177: * @exception UnsupportedOperationException is thrown when the project does not support
178: * adding of an artifact to the classpath of the given type.
179: */
180: protected abstract boolean addAntArtifacts(AntArtifact[] artifacts,
181: URI[] artifactElements, SourceGroup sourceGroup, String type)
182: throws IOException, UnsupportedOperationException;
183:
184: /**
185: * Removes artifacts (e.g. subprojects) from project's classpath if the
186: * artifacts are included on it.
187: * @param artifacts to be added
188: * @param artifactElements the URIs of the build output, the artifactElements has to have the same length
189: * as artifacts.
190: * (must be owned by the artifact and be relative to it)
191: * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA}
192: * identifying the compilation unit to change
193: * @param type the type of the classpath the artifact should be removed from,
194: * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE}
195: * @return true in case the classpath was changed, (at least one artifact was removed from the classpath),
196: * the value false is returned when none of the artifacts was included on the classpath.
197: * @exception IOException in case the project metadata cannot be changed
198: * @exception UnsupportedOperationException is thrown when the project does not support
199: * removing of an artifact from the classpath of the given type.
200: */
201: protected abstract boolean removeAntArtifacts(
202: AntArtifact[] artifacts, URI[] artifactElements,
203: SourceGroup sourceGroup, String type) throws IOException,
204: UnsupportedOperationException;
205:
206: /**
207: * Takes a classpath root and tries to figure the best way to reference that file for that particular project.
208: * The possible actions include relativization of path, copying to sharable libraries folder etc.
209: * @param classpathRoot passed in through the <code>addRoots()</code> and <code>removeRoots()</code> methods
210: * @param helper
211: * @return a relative or absolute path to the original jar/folder or to a copy of it.
212: * @throws java.net.URISyntaxException
213: * @since org.netbeans.modules.java.project/1 1.15
214: */
215: protected final String performSharabilityHeuristics(
216: URL classpathRoot, AntProjectHelper helper)
217: throws URISyntaxException, IOException {
218: assert classpathRoot != null;
219: assert classpathRoot.toExternalForm().endsWith("/"); //NOI18N
220: URL toAdd = FileUtil.getArchiveFile(classpathRoot);
221: if (toAdd == null) {
222: toAdd = classpathRoot;
223: }
224: File prjRoot = FileUtil.toFile(helper.getProjectDirectory());
225: final File file = PropertyUtils.resolveFile(prjRoot,
226: LibrariesSupport.convertURLToFilePath(toAdd));
227: String f;
228: if (CollocationQuery.areCollocated(file, prjRoot)) {
229: //colocated get always relative path
230: f = PropertyUtils.relativizeFile(prjRoot, file);
231: } else {
232: if (helper.isSharableProject()) {
233: //library location is not collocated..
234: //-> use absolute path
235: // sort of heuristics to have the
236: File library = PropertyUtils.resolveFile(prjRoot,
237: helper.getLibrariesLocation());
238: boolean fileLibraryCol = CollocationQuery
239: .areCollocated(library.getParentFile(), file);
240: boolean libraryAbsolute = LibrariesSupport
241: .isAbsoluteURL(LibrariesSupport
242: .convertFilePathToURL(helper
243: .getLibrariesLocation()));
244: // when library location is absolute, we are most probably dealing with the famous X: drive location
245: // since the library is absolute, it shoudl be safe to reference everything under it as absolute as well.
246: if (libraryAbsolute && fileLibraryCol) {
247: f = file.getAbsolutePath();
248: } else if (libraryAbsolute && !fileLibraryCol) {
249: File fl = copyFile(file, FileUtil
250: .toFileObject(library.getParentFile()));
251: f = fl.getAbsolutePath();
252: } else if (!libraryAbsolute && fileLibraryCol) {
253: f = PropertyUtils.relativizeFile(prjRoot, file);
254: } else { // if (!libraryAbsolute && !fileLibraryCol)
255: File fl = copyFile(file, FileUtil
256: .toFileObject(library.getParentFile()));
257: f = PropertyUtils.relativizeFile(prjRoot, fl);
258: }
259: } else {
260: //nonsharable project are ok with absolute path
261: f = file.getAbsolutePath();
262: }
263: }
264: return f;
265: }
266:
267: private File copyFile(File file, FileObject newRoot)
268: throws IOException {
269: FileObject fo = FileUtil.toFileObject(file);
270: if (fo.isFolder()) {
271: return FileUtil.toFile(copyFolderRecursively(fo, newRoot));
272: } else {
273: FileObject foExists = newRoot.getFileObject(fo.getName(),
274: fo.getExt());
275: if (foExists != null) {
276: foExists.delete();
277: }
278: return FileUtil.toFile(FileUtil.copyFile(fo, newRoot, fo
279: .getName(), fo.getExt()));
280: }
281: }
282:
283: //copied from FileChooserAccessory
284: private FileObject copyFolderRecursively(FileObject sourceFolder,
285: FileObject destination) throws IOException {
286: assert sourceFolder.isFolder() : sourceFolder;
287: assert destination.isFolder() : destination;
288: FileObject destinationSubFolder = destination
289: .getFileObject(sourceFolder.getName());
290: if (destinationSubFolder == null) {
291: destinationSubFolder = destination
292: .createFolder(sourceFolder.getName());
293: }
294: for (FileObject fo : sourceFolder.getChildren()) {
295: if (fo.isFolder()) {
296: copyFolderRecursively(fo, destinationSubFolder);
297: } else {
298: FileObject foExists = destinationSubFolder
299: .getFileObject(fo.getName(), fo.getExt());
300: if (foExists != null) {
301: foExists.delete();
302: }
303: FileUtil.copyFile(fo, destinationSubFolder, fo
304: .getName(), fo.getExt());
305: }
306: }
307: return destinationSubFolder;
308: }
309:
310: private static class Accessor extends
311: ProjectClassPathModifierAccessor {
312:
313: public SourceGroup[] getExtensibleSourceGroups(
314: final ProjectClassPathModifierImplementation m) {
315: assert m != null;
316: return m.getExtensibleSourceGroups();
317: }
318:
319: public String[] getExtensibleClassPathTypes(
320: final ProjectClassPathModifierImplementation m,
321: SourceGroup sg) {
322: assert m != null;
323: assert sg != null;
324: return m.getExtensibleClassPathTypes(sg);
325: }
326:
327: public boolean removeLibraries(Library[] libraries,
328: ProjectClassPathModifierImplementation m,
329: SourceGroup sourceGroup, String type)
330: throws IOException, UnsupportedOperationException {
331: assert m != null;
332: return m.removeLibraries(libraries, sourceGroup, type);
333: }
334:
335: public boolean removeAntArtifacts(AntArtifact[] artifacts,
336: URI[] artifactElements,
337: ProjectClassPathModifierImplementation m,
338: SourceGroup sourceGroup, String type)
339: throws IOException, UnsupportedOperationException {
340: assert m != null;
341: return m.removeAntArtifacts(artifacts, artifactElements,
342: sourceGroup, type);
343: }
344:
345: public boolean addLibraries(Library[] libraries,
346: ProjectClassPathModifierImplementation m,
347: SourceGroup sourceGroup, String type)
348: throws IOException, UnsupportedOperationException {
349: assert m != null;
350: return m.addLibraries(libraries, sourceGroup, type);
351: }
352:
353: public boolean addAntArtifacts(AntArtifact[] artifacts,
354: URI[] artifactElements,
355: ProjectClassPathModifierImplementation m,
356: SourceGroup sourceGroup, String type)
357: throws IOException, UnsupportedOperationException {
358: assert m != null;
359: return m.addAntArtifacts(artifacts, artifactElements,
360: sourceGroup, type);
361: }
362:
363: public boolean removeRoots(URL[] classPathRoots,
364: ProjectClassPathModifierImplementation m,
365: SourceGroup sourceGroup, String type)
366: throws IOException, UnsupportedOperationException {
367: assert m != null;
368: return m.removeRoots(classPathRoots, sourceGroup, type);
369: }
370:
371: public boolean addRoots(URL[] classPathRoots,
372: ProjectClassPathModifierImplementation m,
373: SourceGroup sourceGroup, String type)
374: throws IOException, UnsupportedOperationException {
375: assert m != null;
376: return m.addRoots(classPathRoots, sourceGroup, type);
377: }
378:
379: }
380: }
|