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