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.JavadocForBinaryQueryLibraryImpl
043: package org.netbeans.modules.visualweb.project.jsf.libraries;
044:
045: import java.beans.PropertyChangeEvent;
046:
047: import java.beans.PropertyChangeListener;
048:
049: import java.net.URL;
050: import java.util.ArrayList;
051: import java.util.Iterator;
052: import java.util.List;
053: import java.util.Map;
054: import java.util.HashMap;
055: import javax.swing.event.ChangeEvent;
056:
057: import javax.swing.event.ChangeListener;
058:
059: import org.openide.ErrorManager;
060: import org.openide.util.WeakListeners;
061: import org.openide.filesystems.URLMapper;
062: import org.openide.filesystems.FileObject;
063: import org.openide.filesystems.FileStateInvalidException;
064: import org.openide.filesystems.FileUtil;
065: import org.netbeans.api.java.queries.JavadocForBinaryQuery;
066: import org.netbeans.api.project.libraries.Library;
067: import org.netbeans.api.project.libraries.LibraryManager;
068: import org.netbeans.spi.java.queries.JavadocForBinaryQueryImplementation; // <RAVE>
069: // import org.netbeans.modules.java.j2seplatform.platformdefinition.Util;
070: import org.netbeans.modules.visualweb.project.jsf.libraries.provider.ComponentLibraryTypeProvider;
071: import org.netbeans.spi.project.libraries.support.LibrariesSupport;
072:
073: /**
074: * Implementation of Javadoc query for the library.
075: * @author Po-Ting Wu
076: */
077: public class JavadocForBinaryQueryLibraryImpl implements
078: JavadocForBinaryQueryImplementation {
079:
080: private static int MAX_DEPTH = 3;
081: private final Map/*<URL,URL>*/normalizedURLCache = new HashMap();
082:
083: /** Default constructor for lookup. */
084: public JavadocForBinaryQueryLibraryImpl() {
085: }
086:
087: public JavadocForBinaryQuery.Result findJavadoc(final URL b) {
088: class R implements JavadocForBinaryQuery.Result,
089: PropertyChangeListener {
090:
091: private Library lib;
092: private ArrayList listeners;
093: private URL[] cachedRoots;
094:
095: public R(Library lib) {
096: this .lib = lib;
097: this .lib.addPropertyChangeListener(WeakListeners
098: .propertyChange(this , this .lib));
099: }
100:
101: public synchronized URL[] getRoots() {
102: if (this .cachedRoots == null) {
103: List l = lib
104: .getContent(ComponentLibraryTypeProvider.VOLUME_TYPE_JAVADOC);
105: List result = new ArrayList();
106: for (Iterator it = l.iterator(); it.hasNext();) {
107: URL u = (URL) it.next();
108: u = LibrariesSupport.resolveLibraryEntryURL(lib
109: .getManager().getLocation(), u);
110: result.add(getIndexFolder(u));
111: }
112: this .cachedRoots = (URL[]) result
113: .toArray(new URL[result.size()]);
114: }
115: return this .cachedRoots;
116: }
117:
118: public synchronized void addChangeListener(ChangeListener l) {
119: assert l != null : "Listener can not be null";
120: if (this .listeners == null) {
121: this .listeners = new ArrayList();
122: }
123: this .listeners.add(l);
124: }
125:
126: public synchronized void removeChangeListener(
127: ChangeListener l) {
128: assert l != null : "Listener can not be null";
129: if (this .listeners == null) {
130: return;
131: }
132: this .listeners.remove(l);
133: }
134:
135: public void propertyChange(PropertyChangeEvent event) {
136: if (Library.PROP_CONTENT
137: .equals(event.getPropertyName())) {
138: synchronized (this ) {
139: this .cachedRoots = null;
140: }
141: this .fireChange();
142: }
143: }
144:
145: private void fireChange() {
146: Iterator it = null;
147: synchronized (this ) {
148: if (this .listeners == null) {
149: return;
150: }
151: it = ((ArrayList) this .listeners.clone())
152: .iterator();
153: }
154: ChangeEvent event = new ChangeEvent(this );
155: while (it.hasNext()) {
156: ((ChangeListener) it.next()).stateChanged(event);
157: }
158: }
159: }
160:
161: boolean isNormalizedURL = isNormalizedURL(b);
162: for (LibraryManager lm : LibraryManager.getOpenManagers()) {
163: Library[] libs = lm.getLibraries();
164: for (int i = 0; i < libs.length; i++) {
165: String type = libs[i].getType();
166: if (!ComponentLibraryTypeProvider.LIBRARY_TYPE
167: .equalsIgnoreCase(type)) {
168: continue;
169: }
170: // <RAVE> Only search the one that has 'javadoc' volume defined
171: List javadoc = libs[i]
172: .getContent(ComponentLibraryTypeProvider.VOLUME_TYPE_JAVADOC);
173: if (javadoc.size() == 0) {
174: continue;
175: }
176: // </RAVE>
177: List jars = libs[i]
178: .getContent(ComponentLibraryTypeProvider.VOLUME_TYPE_CLASSPATH); //NOI18N
179: Iterator it = jars.iterator();
180: while (it.hasNext()) {
181: URL entry = (URL) it.next();
182: entry = LibrariesSupport.resolveLibraryEntryURL(lm
183: .getLocation(), entry);
184: URL normalizedEntry;
185: if (isNormalizedURL) {
186: normalizedEntry = getNormalizedURL(entry);
187: } else {
188: normalizedEntry = entry;
189: }
190: if (normalizedEntry != null
191: && normalizedEntry.equals(b)) {
192: return new R(libs[i]);
193: }
194: }
195: }
196: }
197: return null;
198: }
199:
200: private URL getNormalizedURL(URL url) {
201: //URL is already nornalized, return it
202: if (isNormalizedURL(url)) {
203: return url;
204: }
205: //Todo: Should listen on the LibrariesManager and cleanup cache
206: // in this case the search can use the cache onle and can be faster
207: // from O(n) to O(ln(n))
208: URL normalizedURL = (URL) this .normalizedURLCache.get(url);
209: if (normalizedURL == null) {
210: FileObject fo = URLMapper.findFileObject(url);
211: if (fo != null) {
212: try {
213: normalizedURL = fo.getURL();
214: this .normalizedURLCache.put(url, normalizedURL);
215: } catch (FileStateInvalidException e) {
216: ErrorManager.getDefault().notify(e);
217: }
218: }
219: }
220: return normalizedURL;
221: }
222:
223: /**
224: * Returns true if the given URL is file based, it is already
225: * resolved either into file URL or jar URL with file path.
226: * @param URL url
227: * @return true if the URL is normal
228: */
229: private static boolean isNormalizedURL(URL url) {
230: if ("jar".equals(url.getProtocol())) { //NOI18N
231: url = FileUtil.getArchiveFile(url);
232: }
233: return "file".equals(url.getProtocol()); //NOI18N
234: }
235:
236: /**
237: * Tests if the query accepts the root as valid JavadocRoot,
238: * the query accepts the JavaDoc root, if it can find the index-files
239: * or index-all.html in the root.
240: * @param rootURL the javadoc root
241: * @return true if the root is a valid Javadoc root
242: */
243: static boolean isValidLibraryJavadocRoot(final URL rootURL) {
244: assert rootURL != null
245: && rootURL.toExternalForm().endsWith("/");
246: final FileObject root = URLMapper.findFileObject(rootURL);
247: if (root == null) {
248: return false;
249: }
250: return findIndexFolder(root, 1) != null;
251: }
252:
253: /**
254: * Search for the actual root of the Javadoc containing the index-all.html or
255: * index-files. In case when it is not able to find it, it returns the given Javadoc folder/file.
256: * @param URL Javadoc folder/file
257: * @return URL either the URL of folder containg the index or the given parameter if the index was not found.
258: */
259: private static URL getIndexFolder(URL rootURL) {
260: if (rootURL == null) {
261: return null;
262: }
263: FileObject root = URLMapper.findFileObject(rootURL);
264: if (root == null) {
265: return rootURL;
266: }
267: FileObject result = findIndexFolder(root, 1);
268: try {
269: return result == null ? rootURL : result.getURL();
270: } catch (FileStateInvalidException e) {
271: ErrorManager.getDefault().notify(e);
272: return rootURL;
273: }
274: }
275:
276: private static FileObject findIndexFolder(FileObject fo, int depth) {
277: if (depth > MAX_DEPTH) {
278: return null;
279: }
280: if (fo.getFileObject("index-files", null) != null
281: || fo.getFileObject("index-all.html", null) != null) { //NOI18N
282: return fo;
283: }
284: FileObject[] children = fo.getChildren();
285: for (int i = 0; i < children.length; i++) {
286: if (children[i].isFolder()) {
287: FileObject result = findIndexFolder(children[i],
288: depth + 1);
289: if (result != null) {
290: return result;
291: }
292: }
293: }
294: return null;
295: }
296:
297: }
|