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.j2ee;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.io.File;
047: import java.io.IOException;
048: import java.net.URI;
049: import java.net.URL;
050: import java.util.ArrayList;
051: import java.util.Iterator;
052: import java.util.List;
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.ant.AntArtifact;
057: import org.netbeans.api.project.libraries.Library;
058: import org.netbeans.api.project.libraries.LibraryManager;
059: import org.netbeans.modules.java.api.common.util.CommonProjectUtils;
060: import org.netbeans.modules.java.api.common.SourceRoots;
061: import org.netbeans.modules.java.api.common.ant.UpdateHelper;
062: import org.netbeans.modules.java.api.common.classpath.ProjectClassPathModifierSupport;
063: import org.netbeans.modules.java.api.common.classpath.ProjectClassPathModifierSupport.Operation;
064: import org.netbeans.modules.java.api.common.classpath.ClassPathItem;
065: import org.netbeans.modules.java.api.common.classpath.ClassPathSupport;
066: import org.netbeans.modules.java.api.common.classpath.ProjectClassPathModifierSupport.ClassPathItemProvider;
067: import org.netbeans.modules.java.api.common.classpath.ProjectClassPathModifierSupport.Properties;
068: import org.netbeans.spi.java.project.classpath.ProjectClassPathModifierImplementation;
069: import org.netbeans.spi.project.support.ant.AntProjectHelper;
070: import org.netbeans.spi.project.support.ant.EditableProperties;
071: import org.netbeans.spi.project.support.ant.PropertyEvaluator;
072: import org.netbeans.spi.project.support.ant.ReferenceHelper;
073: import org.openide.util.Exceptions;
074: import org.openide.util.Mutex;
075: import org.openide.util.MutexException;
076: import org.openide.util.Parameters;
077: import org.openide.util.RequestProcessor;
078: import org.openide.util.WeakListeners;
079:
080: // will be moved to j2ee.common
081: /**
082: *@author Tomas Zezula, Tomas Mysik
083: */
084: public class J2EEProjectClassPathModifier extends
085: ProjectClassPathModifierImplementation implements
086: PropertyChangeListener {
087:
088: // XXX ok or not?
089: private static final String JAVAC_CLASSPATH = "javac.classpath"; // NOI18N
090: private static final String ELEMENT_INCLUDED_LIBRARIES = "included-library"; // NOI18N
091:
092: private final Project project;
093: private final UpdateHelper helper;
094: private final PropertyEvaluator eval;
095: private final J2EEClassPathSupport classPathSupport;
096: private final ProjectClassPathModifierSupport<J2EEClassPathSupport.Item> classPathModifierSupport;
097: private final ClassPathItemProvider<J2EEClassPathSupport.Item> provider;
098:
099: private volatile boolean projectDeleted;
100:
101: private final PropertyChangeListener listener = WeakListeners
102: .propertyChange(this , null);
103:
104: // XXX javadoc
105: public static J2EEProjectClassPathModifier create(Project project,
106: UpdateHelper helper, PropertyEvaluator eval,
107: ReferenceHelper refHelper,
108: J2EEClassPathSupport classPathSupport,
109: SourceRoots sourceRoots, SourceRoots testSourceRoots,
110: Properties properties) {
111: Parameters.notNull("project", project);
112: Parameters.notNull("helper", helper);
113: Parameters.notNull("eval", eval);
114: Parameters.notNull("classPathSupport", classPathSupport);
115:
116: return new J2EEProjectClassPathModifier(project, helper, eval,
117: refHelper, classPathSupport, sourceRoots,
118: testSourceRoots, properties);
119: }
120:
121: J2EEProjectClassPathModifier(Project project, UpdateHelper helper,
122: PropertyEvaluator eval, ReferenceHelper refHelper,
123: J2EEClassPathSupport classPathSupport,
124: SourceRoots sourceRoots, SourceRoots testSourceRoots,
125: Properties properties) {
126: assert project != null;
127: assert helper != null;
128: assert eval != null;
129: assert classPathSupport != null;
130:
131: this .project = project;
132: this .helper = helper;
133: this .eval = eval;
134: this .classPathSupport = classPathSupport;
135: classPathModifierSupport = ProjectClassPathModifierSupport
136: .<J2EEClassPathSupport.Item> create(project, helper,
137: eval, refHelper, sourceRoots, testSourceRoots,
138: properties);
139: provider = new J2EEClassPathItemProvider(
140: ELEMENT_INCLUDED_LIBRARIES);
141: //#56140
142: eval.addPropertyChangeListener(listener); //listen for changes of libraries list
143: ProjectManager.mutex().postWriteRequest(new Runnable() {
144: public void run() {
145: registerLibraryListeners();
146: }
147: });
148: }
149:
150: final class J2EEClassPathItemProvider implements
151: ClassPathItemProvider<J2EEClassPathSupport.Item> {
152:
153: private final String elementName;
154:
155: public J2EEClassPathItemProvider(String elementName) {
156: this .elementName = elementName;
157: }
158:
159: public J2EEClassPathSupport.Item createClassPathItem(File file,
160: String property) {
161: return J2EEClassPathSupport.Item.create(file, null,
162: property != null);
163: }
164:
165: public J2EEClassPathSupport.Item createClassPathItem(
166: AntArtifact antArtifact, URI antArtifactURI,
167: String property) {
168: return J2EEClassPathSupport.Item.create(antArtifact,
169: antArtifactURI, null, property != null);
170: }
171:
172: public J2EEClassPathSupport.Item createClassPathItem(
173: Library library, String property) {
174: return J2EEClassPathSupport.Item.create(library, null,
175: property != null);
176: }
177:
178: public List<J2EEClassPathSupport.Item> getClassPathItems(
179: String reference) {
180: return classPathSupport.itemsList(reference, elementName);
181: }
182:
183: public String[] encodeToStrings(
184: List<J2EEClassPathSupport.Item> items) {
185: return classPathSupport.encodeToStrings(items, elementName);
186: }
187:
188: public String getLibraryReference(J2EEClassPathSupport.Item item) {
189: return classPathSupport.getLibraryReference(item);
190: }
191: }
192:
193: @Override
194: protected SourceGroup[] getExtensibleSourceGroups() {
195: return classPathModifierSupport.getExtensibleSourceGroups();
196: }
197:
198: @Override
199: protected String[] getExtensibleClassPathTypes(
200: SourceGroup sourceGroup) {
201: return classPathModifierSupport
202: .getExtensibleClassPathTypes(sourceGroup);
203: }
204:
205: @Override
206: protected boolean removeRoots(final URL[] classPathRoots,
207: final SourceGroup sourceGroup, final String type)
208: throws IOException, UnsupportedOperationException {
209: String classPathProperty = classPathModifierSupport
210: .getClassPathProperty(sourceGroup, type);
211: return classPathModifierSupport.handleRoots(classPathRoots,
212: classPathProperty, Operation.ADD, provider);
213: }
214:
215: @Override
216: protected boolean addRoots(final URL[] classPathRoots,
217: final SourceGroup sourceGroup, final String type)
218: throws IOException, UnsupportedOperationException {
219: String classPathProperty = classPathModifierSupport
220: .getClassPathProperty(sourceGroup, type);
221: return classPathModifierSupport.handleRoots(classPathRoots,
222: classPathProperty, Operation.ADD, provider);
223: }
224:
225: // used by ClassPathExtender
226: protected boolean handleRoots(URL[] classPathRoots,
227: String classPathProperty, String elementName,
228: Operation operation) throws IOException,
229: UnsupportedOperationException {
230: return classPathModifierSupport.handleRoots(classPathRoots,
231: classPathProperty, operation, provider);
232: }
233:
234: @Override
235: protected boolean removeAntArtifacts(final AntArtifact[] artifacts,
236: final URI[] artifactElements,
237: final SourceGroup sourceGroup, final String type)
238: throws IOException, UnsupportedOperationException {
239: String classPathProperty = classPathModifierSupport
240: .getClassPathProperty(sourceGroup, type);
241: return classPathModifierSupport.handleAntArtifacts(artifacts,
242: artifactElements, classPathProperty, Operation.REMOVE,
243: provider);
244: }
245:
246: @Override
247: protected boolean addAntArtifacts(final AntArtifact[] artifacts,
248: final URI[] artifactElements,
249: final SourceGroup sourceGroup, final String type)
250: throws IOException, UnsupportedOperationException {
251: String classPathProperty = classPathModifierSupport
252: .getClassPathProperty(sourceGroup, type);
253: return classPathModifierSupport.handleAntArtifacts(artifacts,
254: artifactElements, classPathProperty, Operation.ADD,
255: provider);
256: }
257:
258: @Override
259: protected boolean removeLibraries(final Library[] libraries,
260: final SourceGroup sourceGroup, final String type)
261: throws IOException, UnsupportedOperationException {
262: String classPathProperty = classPathModifierSupport
263: .getClassPathProperty(sourceGroup, type);
264: return handleLibraries(libraries, classPathProperty,
265: Operation.REMOVE, provider);
266: }
267:
268: @Override
269: protected boolean addLibraries(final Library[] libraries,
270: final SourceGroup sourceGroup, final String type)
271: throws IOException, UnsupportedOperationException {
272: String classPathProperty = classPathModifierSupport
273: .getClassPathProperty(sourceGroup, type);
274: return handleLibraries(libraries, classPathProperty,
275: Operation.ADD, provider);
276: }
277:
278: // used by ClassPathExtender
279: protected boolean handleLibraries(final Library[] libraries,
280: final String classPathProperty, final String elementName,
281: final Operation operation) throws IOException,
282: UnsupportedOperationException {
283: return handleLibraries(libraries, classPathProperty, operation,
284: new J2EEClassPathItemProvider(elementName));
285: }
286:
287: // XXX check this - see saving of private properties
288: public boolean handleLibraries(
289: final Library[] libraries,
290: final String classPathProperty,
291: final Operation operation,
292: final ClassPathItemProvider<J2EEClassPathSupport.Item> provider)
293: throws IOException, UnsupportedOperationException {
294: assert libraries != null : "Libraries cannot be null";
295: assert classPathProperty != null;
296:
297: try {
298: return ProjectManager.mutex().writeAccess(
299: new Mutex.ExceptionAction<Boolean>() {
300: public Boolean run() throws IOException {
301: EditableProperties props = helper
302: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
303: String raw = props
304: .getProperty(classPathProperty);
305: List<J2EEClassPathSupport.Item> resources = provider
306: .getClassPathItems(raw);
307: List<J2EEClassPathSupport.Item> changed = new ArrayList<J2EEClassPathSupport.Item>(
308: libraries.length);
309: for (Library library : libraries) {
310: assert library != null;
311:
312: J2EEClassPathSupport.Item item = provider
313: .createClassPathItem(library,
314: null);
315: if (operation == Operation.ADD
316: && !resources.contains(item)) {
317: resources.add(item);
318: changed.add(item);
319: } else if (operation == Operation.REMOVE
320: && resources.contains(item)) {
321: resources.remove(item);
322: changed.add(item);
323: }
324: }
325: if (!changed.isEmpty()) {
326: String[] itemRefs = provider
327: .encodeToStrings(resources);
328: // PathParser may change the EditableProperties
329: props = helper
330: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
331: props.setProperty(classPathProperty,
332: itemRefs);
333: if (operation == Operation.ADD) {
334: for (J2EEClassPathSupport.Item item : changed) {
335: String prop = provider
336: .getLibraryReference(item);
337: // XXX make a PropertyUtils method for this!
338: prop = prop.substring(2, prop
339: .length() - 1);
340: ClassPathSupport
341: .relativizeLibraryClassPath(
342: props,
343: helper
344: .getAntProjectHelper(),
345: prop);
346: }
347: }
348: helper
349: .putProperties(
350: AntProjectHelper.PROJECT_PROPERTIES_PATH,
351: props);
352:
353: // XXX really needed? if not, ProjectClassPathModifierSupport could be used
354: // update lib references in private properties
355: EditableProperties privateProps = helper
356: .getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH);
357: List<J2EEClassPathSupport.Item> libs = new ArrayList<J2EEClassPathSupport.Item>();
358: libs.addAll(resources);
359: storeLibrariesLocations(
360: libs.iterator(), privateProps);
361: helper
362: .putProperties(
363: AntProjectHelper.PRIVATE_PROPERTIES_PATH,
364: privateProps);
365:
366: ProjectManager.getDefault()
367: .saveProject(project);
368: return true;
369: }
370: return false;
371: }
372: });
373: } catch (MutexException e) {
374: throw (IOException) e.getException();
375: }
376: }
377:
378: private void registerLibraryListeners() {
379: // reread the properties, PathParser changes them
380: EditableProperties props = helper
381: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
382: Library[] libs = LibraryManager.getDefault().getLibraries();
383: for (Library library : libs) {
384: library.removePropertyChangeListener(this );
385: }
386: Iterator<J2EEClassPathSupport.Item> i = classPathSupport
387: .itemsIterator(props.getProperty(JAVAC_CLASSPATH),
388: ELEMENT_INCLUDED_LIBRARIES);
389: while (i.hasNext()) {
390: J2EEClassPathSupport.Item item = i.next();
391: if (item.getType() == ClassPathItem.Type.LIBRARY
392: && !item.isBroken()) {
393: item.getLibrary().addPropertyChangeListener(this );
394: }
395: }
396: }
397:
398: public void propertyChange(PropertyChangeEvent e) {
399: if (projectDeleted) {
400: return;
401: }
402: if (e.getSource().equals(eval)
403: && (e.getPropertyName().equals(JAVAC_CLASSPATH))) {
404: // reread the properties, PathParser changes them
405: EditableProperties props = helper
406: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
407: String javacCp = props.getProperty(JAVAC_CLASSPATH);
408: if (javacCp != null) {
409: registerLibraryListeners();
410: storeLibLocations();
411: }
412: } else if (e.getPropertyName().equals(Library.PROP_CONTENT)) {
413: storeLibLocations();
414: }
415: }
416:
417: private void storeLibLocations() {
418: RequestProcessor.getDefault().post(new Runnable() {
419: public void run() {
420: ProjectManager.mutex().writeAccess(new Runnable() {
421: public void run() {
422: // reread the properties, PathParser changes them
423: EditableProperties props = helper
424: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
425: // update lib references in private properties
426: EditableProperties privateProps = helper
427: .getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH);
428: List<J2EEClassPathSupport.Item> wmLibs = classPathSupport
429: .itemsList(props
430: .getProperty(JAVAC_CLASSPATH),
431: ELEMENT_INCLUDED_LIBRARIES);
432: classPathSupport.encodeToStrings(wmLibs,
433: ELEMENT_INCLUDED_LIBRARIES);
434: storeLibrariesLocations(wmLibs.iterator(),
435: privateProps);
436: helper
437: .putProperties(
438: AntProjectHelper.PRIVATE_PROPERTIES_PATH,
439: privateProps);
440:
441: try {
442: ProjectManager.getDefault().saveProject(
443: project);
444: } catch (IOException e) {
445: Exceptions.printStackTrace(e);
446: }
447: }
448: });
449: }
450: });
451: }
452:
453: public void notifyDeleting() {
454: projectDeleted = true;
455: eval.removePropertyChangeListener(this );
456: }
457:
458: /** Store locations of libraries in the classpath param that have more the one
459: * file into the properties in the following format:
460: *
461: * <ul>
462: * <li>libs.foo.classpath.libdir.1=C:/foo
463: * <li>libs.foo.classpath.libdirs=1
464: * <li>libs.foo.classpath.libfile.1=C:/bar/a.jar
465: * <li>libs.foo.classpath.libfile.2=C:/bar/b.jar
466: * <li>libs.foo.classpath.libfiles=1
467: * </ul>
468: * This is needed for the Ant copy task as it cannot copy more the one file
469: * and it needs different handling for files and directories.
470: * <br>
471: * It removes all properties that match this format that were in the {@link #properties}
472: * but are not in the {@link #classpath}.
473: */
474: public static void storeLibrariesLocations(
475: Iterator<J2EEClassPathSupport.Item> classpath,
476: EditableProperties privateProps) {
477: List<String> exLibs = new ArrayList<String>();
478: for (String key : privateProps.keySet()) {
479: if (key.endsWith(".libdirs") || key.endsWith(".libfiles")
480: || // NOI18N
481: (key.indexOf(".libdir.") > 0)
482: || (key.indexOf(".libfile.") > 0)) { // NOI18N
483: exLibs.add(key);
484: }
485: }
486: while (classpath.hasNext()) {
487: J2EEClassPathSupport.Item item = classpath.next();
488: List<File> files = new ArrayList<File>();
489: List<File> dirs = new ArrayList<File>();
490: J2EEClassPathSupport.getFilesForItem(item, files, dirs);
491: String key;
492: if (files.size() > 1
493: || (files.size() > 0 && dirs.size() > 0)) {
494: String ref = item.getType() == ClassPathItem.Type.LIBRARY ? item
495: .getRaw()
496: : item.getReference();
497: for (int i = 0; i < files.size(); i++) {
498: File f = files.get(i);
499: key = CommonProjectUtils.getAntPropertyName(ref)
500: + ".libfile." + (i + 1); // NOI18N
501: privateProps.setProperty(key, ""
502: + f.getAbsolutePath()); // NOI18N
503: exLibs.remove(key);
504: }
505: }
506: if (dirs.size() > 1
507: || (files.size() > 0 && dirs.size() > 0)) {
508: String ref = item.getType() == ClassPathItem.Type.LIBRARY ? item
509: .getRaw()
510: : item.getReference();
511: for (int i = 0; i < dirs.size(); i++) {
512: File f = dirs.get(i);
513: key = CommonProjectUtils.getAntPropertyName(ref)
514: + ".libdir." + (i + 1); // NOI18N
515: privateProps.setProperty(key, ""
516: + f.getAbsolutePath()); // NOI18N
517: exLibs.remove(key);
518: }
519: }
520: }
521: Iterator unused = exLibs.iterator();
522: while (unused.hasNext()) {
523: privateProps.remove(unused.next());
524: }
525: }
526: }
|