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: // <RAVE> Copy of org.netbeans.modules.java.j2seplatform.libraries.J2SELibrarySourceForBinaryQuery
043: package org.netbeans.modules.visualweb.project.jsf.libraries;
044:
045: import java.beans.PropertyChangeEvent;
046: import java.beans.PropertyChangeListener;
047: import java.net.URL;
048: import java.util.ArrayList;
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.Map;
052: import java.util.HashMap;
053: import javax.swing.event.ChangeEvent;
054: import javax.swing.event.ChangeListener;
055: import org.netbeans.api.java.queries.SourceForBinaryQuery;
056: import org.netbeans.api.project.libraries.Library;
057: import org.netbeans.api.project.libraries.LibraryManager;
058: import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation;
059: import org.openide.ErrorManager;
060: import org.openide.filesystems.FileObject;
061: import org.openide.filesystems.FileStateInvalidException;
062: import org.openide.filesystems.FileUtil;
063: import org.openide.filesystems.URLMapper;
064: import org.openide.util.WeakListeners; // <RAVE>
065: import org.netbeans.modules.visualweb.project.jsf.libraries.provider.ComponentLibraryTypeProvider;
066: import org.netbeans.spi.project.libraries.support.LibrariesSupport;
067:
068: /**
069: * Finds the locations of sources for various libraries.
070: * @author Po-Ting Wu
071: */
072: public class SourceForBinaryQueryLibraryImpl implements
073: SourceForBinaryQueryImplementation {
074:
075: private final Map/*<URL,SourceForBinaryQuery.Result>*/cache = new HashMap();
076: private final Map/*<URL,URL>*/normalizedURLCache = new HashMap();
077:
078: /** Default constructor for lookup. */
079: public SourceForBinaryQueryLibraryImpl() {
080: }
081:
082: public SourceForBinaryQuery.Result findSourceRoots(URL binaryRoot) {
083: SourceForBinaryQuery.Result res = (SourceForBinaryQuery.Result) this .cache
084: .get(binaryRoot);
085: if (res != null) {
086: return res;
087: }
088: boolean isNormalizedURL = isNormalizedURL(binaryRoot);
089: for (LibraryManager lm : LibraryManager.getOpenManagers()) {
090: Library[] libs = lm.getLibraries();
091: for (int i = 0; i < libs.length; i++) {
092: String type = libs[i].getType();
093: if (ComponentLibraryTypeProvider.LIBRARY_TYPE
094: .equalsIgnoreCase(type)) {
095: // <RAVE> Only search the one that has 'src' volume defined
096: List src = libs[i]
097: .getContent(ComponentLibraryTypeProvider.VOLUME_TYPE_SRC);
098: if (src.size() == 0) {
099: continue;
100: }
101: // </RAVE>
102: List classes = libs[i]
103: .getContent(ComponentLibraryTypeProvider.VOLUME_TYPE_CLASSPATH);
104: for (Iterator it = classes.iterator(); it.hasNext();) {
105: URL entry = (URL) it.next();
106: URL normalizedEntry = LibrariesSupport
107: .resolveLibraryEntryURL(lm
108: .getLocation(), entry);
109: if (isNormalizedURL) {
110: normalizedEntry = getNormalizedURL(entry);
111: } else {
112: normalizedEntry = entry;
113: }
114: if (normalizedEntry != null
115: && normalizedEntry.equals(binaryRoot)) {
116: res = new Result(entry, libs[i]);
117: cache.put(binaryRoot, res);
118: return res;
119: }
120: }
121: }
122: }
123: }
124: return null;
125: }
126:
127: private URL getNormalizedURL(URL url) {
128: //URL is already nornalized, return it
129: if (isNormalizedURL(url)) {
130: return url;
131: }
132: //Todo: Should listen on the LibrariesManager and cleanup cache
133: // in this case the search can use the cache onle and can be faster
134: // from O(n) to O(ln(n))
135: URL normalizedURL = (URL) this .normalizedURLCache.get(url);
136: if (normalizedURL == null) {
137: FileObject fo = URLMapper.findFileObject(url);
138: if (fo != null) {
139: try {
140: normalizedURL = fo.getURL();
141: this .normalizedURLCache.put(url, normalizedURL);
142: } catch (FileStateInvalidException e) {
143: ErrorManager.getDefault().notify(e);
144: }
145: }
146: }
147: return normalizedURL;
148: }
149:
150: /**
151: * Returns true if the given URL is file based, it is already
152: * resolved either into file URL or jar URL with file path.
153: * @param URL url
154: * @return true if the URL is normal
155: */
156: private static boolean isNormalizedURL(URL url) {
157: if ("jar".equals(url.getProtocol())) { //NOI18N
158: url = FileUtil.getArchiveFile(url);
159: }
160: return "file".equals(url.getProtocol()); //NOI18N
161: }
162:
163: private static class Result implements SourceForBinaryQuery.Result,
164: PropertyChangeListener {
165:
166: private Library lib;
167: private URL entry;
168: private ArrayList listeners;
169: private FileObject[] cache;
170:
171: public Result(URL queryFor, Library lib) {
172: this .entry = queryFor;
173: this .lib = lib;
174: this .lib
175: .addPropertyChangeListener((PropertyChangeListener) WeakListeners
176: .create(PropertyChangeListener.class, this ,
177: this .lib));
178: }
179:
180: public synchronized FileObject[] getRoots() {
181: if (this .cache == null) {
182: // entry is not resolved so directly volume content can be searched for it:
183: if (this .lib
184: .getContent(
185: ComponentLibraryTypeProvider.VOLUME_TYPE_CLASSPATH)
186: .contains(entry)) {
187: List<URL> src = this .lib
188: .getContent(ComponentLibraryTypeProvider.VOLUME_TYPE_SRC);
189: List result = new ArrayList();
190: for (URL u : src) {
191: u = LibrariesSupport.resolveLibraryEntryURL(lib
192: .getManager().getLocation(), u);
193: FileObject sourceRootURL = URLMapper
194: .findFileObject(u);
195: if (sourceRootURL != null) {
196: result.add(sourceRootURL);
197: }
198: }
199: this .cache = (FileObject[]) result
200: .toArray(new FileObject[result.size()]);
201: } else {
202: this .cache = new FileObject[0];
203: }
204: }
205: return this .cache;
206: }
207:
208: public synchronized void addChangeListener(ChangeListener l) {
209: assert l != null : "Listener cannot be null"; // NOI18N
210: if (this .listeners == null) {
211: this .listeners = new ArrayList();
212: }
213: this .listeners.add(l);
214: }
215:
216: public synchronized void removeChangeListener(ChangeListener l) {
217: assert l != null : "Listener cannot be null"; // NOI18N
218: if (this .listeners == null) {
219: return;
220: }
221: this .listeners.remove(l);
222: }
223:
224: public void propertyChange(PropertyChangeEvent event) {
225: if (Library.PROP_CONTENT.equals(event.getPropertyName())) {
226: synchronized (this ) {
227: this .cache = null;
228: }
229: this .fireChange();
230: }
231: }
232:
233: private void fireChange() {
234: Iterator it = null;
235: synchronized (this ) {
236: if (this .listeners == null) {
237: return;
238: }
239: it = ((ArrayList) this .listeners.clone()).iterator();
240: }
241: ChangeEvent event = new ChangeEvent(this );
242: while (it.hasNext()) {
243: ((ChangeListener) it.next()).stateChanged(event);
244: }
245: }
246:
247: }
248:
249: }
|