org.netbeans.modules.javadoc.search.JavadocRegistry Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.javadoc.search;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.InputStream;
import java.net.URL;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.io.Reader;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.HTML;
import javax.swing.text.html.parser.ParserDelegator;
import javax.swing.text.MutableAttributeSet;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.netbeans.api.java.classpath.GlobalPathRegistry;
import org.netbeans.api.java.classpath.GlobalPathRegistryEvent;
import org.netbeans.api.java.classpath.GlobalPathRegistryListener;
import org.netbeans.api.java.queries.JavadocForBinaryQuery;
import org.openide.util.BaseUtilities;
import org.openide.util.ChangeSupport;
import org.openide.util.Lookup;
/**
* Class which is able to serve index files of Javadoc for all
* currently used Javadoc documentation sets.
* @author Petr Hrebejk
*/
public class JavadocRegistry implements GlobalPathRegistryListener, ChangeListener, PropertyChangeListener {
private static JavadocRegistry INSTANCE;
private static final Logger LOG = Logger.getLogger(JavadocRegistry.class.getName());
private GlobalPathRegistry regs;
private final ChangeSupport cs = new ChangeSupport(this);
private Set results;
private ClassPath docRoots;
private Set classpaths;
@SuppressWarnings("LeakingThisInConstructor")
private JavadocRegistry() {
this.regs = GlobalPathRegistry.getDefault ();
this.regs.addGlobalPathRegistryListener(this);
}
public static synchronized JavadocRegistry getDefault() {
if ( INSTANCE == null ) {
INSTANCE = new JavadocRegistry();
}
return INSTANCE;
}
/** Returns Array of the Javadoc Index roots
*/
public URL[] getDocRoots() {
synchronized (this) {
if (this.docRoots != null) {
return docRoots();
}
}
//XXX must be called out of synchronized block to prevent
// deadlock. throwCache is called under the ProjectManager.mutex
// write lock and Project's SFBQI requires the ProjectManager.mutex readLock
Set _classpaths = new HashSet();
Set _results = new HashSet();
Set s = readRoots(this, _classpaths, _results);
synchronized (this) {
if (this.docRoots == null) {
this.docRoots = ClassPathSupport.createClassPath(s.toArray(new URL[s.size()]));
this.classpaths = _classpaths;
this.results = _results;
registerListeners(this, _classpaths, _results, this.docRoots);
}
return docRoots();
}
}
private URL[] docRoots() {
List entries = docRoots.entries();
URL[] roots = new URL[entries.size()];
for (int i = 0; i < roots.length; i++) {
roots[i] = entries.get(i).getURL();
}
return roots;
}
public JavadocSearchType findSearchType(URL apidocRoot) {
String encoding = getDocEncoding (apidocRoot);
for (JavadocSearchType jdst : Lookup.getDefault().lookupAll(JavadocSearchType.class)) {
if (jdst.accepts(apidocRoot, encoding)) {
return jdst;
}
}
return null;
}
// Private methods ---------------------------------------------------------
private static Set readRoots(
JavadocRegistry jdr,
Set classpaths,
Set results) {
Set roots = new HashSet();
List paths = new LinkedList();
paths.addAll( jdr.regs.getPaths( ClassPath.COMPILE ) );
paths.addAll( jdr.regs.getPaths( ClassPath.BOOT ) );
for (ClassPath ccp : paths) {
classpaths.add (ccp);
//System.out.println("CCP " + ccp );
for (ClassPath.Entry ccpRoot : ccp.entries()) {
//System.out.println(" CCPR " + ccpRoot.getURL());
JavadocForBinaryQuery.Result result = JavadocForBinaryQuery.findJavadoc(ccpRoot.getURL());
results.add (result);
URL[] jdRoots = result.getRoots();
for (URL jdRoot : jdRoots) {
if(verify(jdRoot)) {
roots.add(jdRoot);
}
}
}
}
//System.out.println("roots=" + roots);
return roots;
}
/**
* Copied and modified from {@link org.netbeans.modules.java.classpath.SimplePathResourceImplementation}
* @param root
*/
private static boolean verify(final URL root) {
if (root == null) {
return false;
}
final String rootS = root.toString();
if (rootS.matches("file:.+[.]jar/?")) { //NOI18N
File f = null;
boolean dir = false;
try {
f = BaseUtilities.toFile(root.toURI());
dir = f.isDirectory();
} catch (URISyntaxException use) {
//pass - handle as non dir
}
if (!dir) {
//Special case for /tmp/build/.jar/ see issue #235695
if (f == null || f.exists() || !f.getName().equals(".jar")) { //NOI18N
return false;
}
}
}
if (!rootS.endsWith("/")) {
return false;
}
if (rootS.contains("/../") || rootS.contains("/./")) { //NOI18N
return false;
}
return true;
}
private static void registerListeners(
JavadocRegistry jdr,
Set classpaths,
Set results,
ClassPath docRoots) {
for (ClassPath cpath : classpaths) {
cpath.addPropertyChangeListener(jdr);
}
for (JavadocForBinaryQuery.Result result : results) {
result.addChangeListener(jdr);
}
docRoots.addPropertyChangeListener (jdr);
}
public @Override void pathsAdded(GlobalPathRegistryEvent event) {
this.throwCache ();
cs.fireChange();
}
public @Override void pathsRemoved(GlobalPathRegistryEvent event) {
this.throwCache ();
cs.fireChange();
}
public @Override void propertyChange (PropertyChangeEvent event) {
if (ClassPath.PROP_ENTRIES.equals (event.getPropertyName()) ||
event.getSource() == this.docRoots) {
this.throwCache ();
cs.fireChange();
}
}
public @Override void stateChanged(ChangeEvent e) {
this.throwCache ();
cs.fireChange();
}
public void addChangeListener(ChangeListener l) {
cs.addChangeListener(l);
}
public void removeChangeListener(ChangeListener l) {
cs.removeChangeListener(l);
}
private synchronized void throwCache () {
//Unregister itself from classpaths, not interested in events
if (classpaths != null) {
for (ClassPath cp : classpaths) {
cp.removePropertyChangeListener(this);
}
classpaths.clear();
}
//Unregister itself from results, not interested in events
if (results != null) {
for (JavadocForBinaryQuery.Result result : results) {
result.removeChangeListener(this);
}
results.clear();
}
//Unregister listener from docRoots
if (docRoots != null) {
docRoots.removePropertyChangeListener(this);
docRoots = null;
}
}
private String getDocEncoding(URL root) {
assert root != null && root.toString().endsWith("/") : root;
InputStream is = URLUtils.open(root, "index-all.html", "index-files/index-1.html");
if (is != null) {
try {
try {
ParserDelegator pd = new ParserDelegator();
BufferedReader in = new BufferedReader(new InputStreamReader(is));
EncodingCallback ecb = new EncodingCallback(in);
pd.parse(in, ecb, true);
return ecb.getEncoding();
} finally {
is.close();
}
} catch (IOException x) {
LOG.log(Level.FINE, "Getting encoding from " + root, x);
}
}
return null;
}
private static class EncodingCallback extends HTMLEditorKit.ParserCallback {
private Reader in;
private String encoding;
public EncodingCallback (Reader in) {
this.in = in;
}
public String getEncoding () {
return this.encoding;
}
public @Override void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
if (t == HTML.Tag.META) {
String value = (String) a.getAttribute(HTML.Attribute.CONTENT);
if (value != null) {
StringTokenizer tk = new StringTokenizer(value,";"); // NOI18N
while (tk.hasMoreTokens()) {
String str = tk.nextToken().trim();
if (str.startsWith("charset")) { //NOI18N
str = str.substring(7).trim();
if (str.charAt(0)=='=') {
this.encoding = str.substring(1).trim();
try {
this.in.close();
} catch (IOException ioe) {/*Ignore it*/}
return;
}
}
}
}
}
}
public @Override void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
if (t == HTML.Tag.BODY) {
try {
this.in.close ();
} catch (IOException ioe) {/*Ignore it*/}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy