Index: source/apichanges.xml =================================================================== RCS file: /cvs/java/source/apichanges.xml,v --- source/apichanges.xml 14 Jun 2007 10:41:03 -0000 1.11 +++ source/apichanges.xml 15 Jun 2007 10:57:08 -0000 @@ -83,6 +83,19 @@ + + + Support for JSPs and dialogs + + + + + + Adding ability to use Java infrastructure for non-Java file (eg. JSP files) and inside dialogs. + + + + ClassIndex methods are cancellable Index: source/nbproject/project.xml =================================================================== RCS file: /cvs/java/source/nbproject/project.xml,v --- source/nbproject/project.xml 7 Jun 2007 08:39:10 -0000 1.19 +++ source/nbproject/project.xml 15 Jun 2007 10:57:08 -0000 @@ -336,5 +336,6 @@ org.netbeans.spi.java.loaders + Index: source/preprocessorbridge/manifest.mf =================================================================== RCS file: /cvs/java/source/preprocessorbridge/manifest.mf,v --- source/preprocessorbridge/manifest.mf 8 Dec 2006 09:36:18 -0000 1.1 +++ source/preprocessorbridge/manifest.mf 15 Jun 2007 10:57:08 -0000 @@ -1,5 +1,5 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.java.preprocessorbridge OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/preprocessorbridge/Bundle.properties -OpenIDE-Module-Specification-Version: 1.0 +OpenIDE-Module-Specification-Version: 1.1 Index: source/preprocessorbridge/nbproject/project.xml =================================================================== RCS file: /cvs/java/source/preprocessorbridge/nbproject/project.xml,v --- source/preprocessorbridge/nbproject/project.xml 8 Dec 2006 09:36:19 -0000 1.1 +++ source/preprocessorbridge/nbproject/project.xml 15 Jun 2007 10:57:08 -0000 @@ -4,10 +4,20 @@ org.netbeans.modules.java.preprocessorbridge - + + + org.openide.filesystems + + + + 7.0 + + + org.netbeans.modules.java.source org.netbeans.modules.mobility.project + org.netbeans.modules.web.core.syntax org.netbeans.modules.java.preprocessorbridge.spi Index: source/preprocessorbridge/src/org/netbeans/modules/java/preprocessorbridge/spi/JavaSourceProvider.java =================================================================== RCS file: source/preprocessorbridge/src/org/netbeans/modules/java/preprocessorbridge/spi/JavaSourceProvider.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ source/preprocessorbridge/src/org/netbeans/modules/java/preprocessorbridge/spi/JavaSourceProvider.java 15 Jun 2007 10:57:08 -0000 @@ -0,0 +1,62 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ +package org.netbeans.modules.java.preprocessorbridge.spi; + +import org.openide.filesystems.FileObject; + +/**Allows creation of JavaSource instances for non-Java files. + * Is expected to produce "virtual" Java source, which is then parsed + * by the Java parser and used by selected Java features. + * + * @author Jan Lahoda, Dusan Balek + */ +public interface JavaSourceProvider { + + /**Create {@link PositionTranslatingJavaFileFilterImplementation} for given file. + * + * @param fo file for which the implementation should be created + * @return PositionTranslatingJavaFileFilterImplementation or null if which provider + * cannot create one for this file + */ + public PositionTranslatingJavaFileFilterImplementation forFileObject(FileObject fo); + + /**"Virtual" Java source provider + * + * Currently, only {@link JavaFileFilterImplementation#filterCharSequence}, + * {@link JavaFileFilterImplementation#getOriginalPosition}, + * {@link JavaFileFilterImplementation#getJavaSourcePosition} are called. + */ + public static interface PositionTranslatingJavaFileFilterImplementation extends JavaFileFilterImplementation { + /**Compute position in the document for given position in the virtual + * Java source. + * + * @param javaSourcePosition position in the virtual Java Source + * @return position in the document + */ + public int getOriginalPosition(int javaSourcePosition); + + /**Compute position in the virtual Java source for given position + * in the document. + * + * @param originalPosition position in the document + * @return position in the virtual Java source + */ + public int getJavaSourcePosition(int originalPosition); + } +} Index: source/src/org/netbeans/api/java/source/CompilationController.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/api/java/source/CompilationController.java,v --- source/src/org/netbeans/api/java/source/CompilationController.java 14 Jun 2007 11:26:14 -0000 1.8 +++ source/src/org/netbeans/api/java/source/CompilationController.java 15 Jun 2007 10:57:08 -0000 @@ -30,8 +30,8 @@ import javax.swing.text.Document; import javax.tools.Diagnostic; import org.netbeans.api.lexer.TokenHierarchy; -import static org.netbeans.api.java.source.JavaSource.Phase.*; import org.openide.filesystems.FileObject; +import static org.netbeans.api.java.source.JavaSource.Phase.*; /** Class for explicit invocation of compilation phases on a java source. * The implementation delegates to the {@link CompilationInfo} to get the data, @@ -175,10 +175,15 @@ public FileObject getFileObject() { return this.delegate.getFileObject(); } - + @Override public Document getDocument() throws IOException { return this.delegate.getDocument(); + } + + @Override + public PositionConverter getPositionConverter() { + return this.delegate.getPositionConverter(); } @Override Index: source/src/org/netbeans/api/java/source/CompilationInfo.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/api/java/source/CompilationInfo.java,v --- source/src/org/netbeans/api/java/source/CompilationInfo.java 6 Jun 2007 11:32:38 -0000 1.12 +++ source/src/org/netbeans/api/java/source/CompilationInfo.java 15 Jun 2007 10:57:08 -0000 @@ -41,14 +41,12 @@ import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.modules.java.source.parsing.FileObjects; import org.netbeans.modules.java.source.parsing.SourceFileObject; -import org.netbeans.modules.java.preprocessorbridge.spi.JavaFileFilterImplementation; import org.openide.ErrorManager; import org.openide.cookies.EditorCookie; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.loaders.DataObject; - /** Asorted information about the JavaSource. * * @author Petr Hrebejk, Tomas Zezula @@ -60,7 +58,7 @@ private List errors; private JavacTaskImpl javacTask; - private FileObject fo; + private PositionConverter binding; final JavaFileObject jfo; final JavaSource javaSource; boolean needsRestart; @@ -76,11 +74,11 @@ this.errors = null; } - CompilationInfo ( final JavaSource javaSource, final FileObject fo, final JavaFileFilterImplementation filter, final JavacTaskImpl javacTask) throws IOException { + CompilationInfo ( final JavaSource javaSource,final PositionConverter binding, final JavacTaskImpl javacTask) throws IOException { assert javaSource != null; this.javaSource = javaSource; - this.fo = fo; - this.jfo = fo != null ? javaSource.jfoProvider.createJavaFileObject(fo, filter) : null; + this.binding = binding; + this.jfo = this.binding != null ? javaSource.jfoProvider.createJavaFileObject(binding.getFileObject(), this.binding.getFilter()) : null; this.javacTask = javacTask; this.errors = new ArrayList(); } @@ -172,7 +170,7 @@ Elements elements = getElements(); assert elements != null; assert this.javaSource.rootFo != null; - String name = FileObjects.convertFolder2Package(FileObjects.stripExtension(FileUtil.getRelativePath(javaSource.rootFo, fo))); + String name = FileObjects.convertFolder2Package(FileObjects.stripExtension(FileUtil.getRelativePath(javaSource.rootFo, getFileObject()))); TypeElement e = ((JavacElements)elements).getTypeElementByBinaryName(name); if (e != null) { if (!isLocal(e)) { @@ -237,17 +235,28 @@ return javaSource.getClasspathInfo(); } - public FileObject getFileObject() { - return fo; + public FileObject getFileObject() { + return this.binding != null ? this.binding.getFileObject() : null; + } + + /**Return {@link PositionConverter} binding virtual Java source and the real source. + * Please note that this method is needed only for clients that need to work + * in non-Java files (eg. JSP files) or in dialogs, like code completion. + * Most clients do not need to use {@link PositionConverter}. + * + * @return PositionConverter binding the virtual Java source and the real source. + */ + public PositionConverter getPositionConverter() { + return binding; } public Document getDocument() throws IOException { - if (this.fo == null) { + if (this.binding == null || this.binding.getFileObject() == null) { return null; } - DataObject od = DataObject.find(fo); - EditorCookie ec = (EditorCookie) od.getCookie(EditorCookie.class); - if (ec != null) { + DataObject od = DataObject.find(this.binding.getFileObject()); + EditorCookie ec = od.getCookie(EditorCookie.class); + if (ec != null) { return ec.getDocument(); } else { return null; Index: source/src/org/netbeans/api/java/source/JavaSource.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/api/java/source/JavaSource.java,v --- source/src/org/netbeans/api/java/source/JavaSource.java 14 Jun 2007 10:41:03 -0000 1.61 +++ source/src/org/netbeans/api/java/source/JavaSource.java 15 Jun 2007 10:57:09 -0000 @@ -92,10 +92,13 @@ import javax.tools.JavaFileObject; import javax.tools.ToolProvider; import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.lexer.JavaTokenId; import org.netbeans.api.java.platform.JavaPlatformManager; import org.netbeans.api.java.queries.SourceLevelQuery; import org.netbeans.api.java.source.ClasspathInfo.PathKind; import org.netbeans.api.java.source.ModificationResult.Difference; +import org.netbeans.api.lexer.Language; +import org.netbeans.api.timers.TimesCollector; import org.netbeans.editor.Registry; import org.netbeans.modules.java.source.JavaFileFilterQuery; import org.netbeans.modules.java.source.builder.ASTService; @@ -106,6 +109,7 @@ import org.netbeans.modules.java.source.engine.RootTree; import org.netbeans.modules.java.source.parsing.FileObjects; import org.netbeans.modules.java.preprocessorbridge.spi.JavaFileFilterImplementation; +import org.netbeans.modules.java.preprocessorbridge.spi.JavaSourceProvider; import org.netbeans.modules.java.source.TreeLoader; import org.netbeans.modules.java.source.builder.DefaultEnvironment; import org.netbeans.modules.java.source.tasklist.CompilerSettings; @@ -130,6 +134,7 @@ import org.openide.loaders.DataObjectNotFoundException; import org.openide.modules.SpecificationVersion; import org.openide.util.Exceptions; +import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; import org.openide.util.WeakListeners; @@ -280,6 +285,8 @@ //Preprocessor support private FilterListener filterListener; + private PositionConverter binding; + private static final Logger LOGGER = Logger.getLogger(JavaSource.class.getName()); static { @@ -299,14 +306,7 @@ if (files == null || cpInfo == null) { throw new IllegalArgumentException (); } - try { - return new JavaSource(cpInfo, files); - } catch (DataObjectNotFoundException donf) { - LOGGER.warning("Ignoring non existent file: " + FileUtil.getFileDisplayName(donf.getFileObject())); //NOI18N - } catch (IOException ex) { - Exceptions.printStackTrace(ex); - } - return null; + return create(cpInfo, null, files); } @@ -322,7 +322,18 @@ if (files == null || cpInfo == null) { throw new IllegalArgumentException (); } - return create(cpInfo, Arrays.asList(files)); + return create(cpInfo, null, Arrays.asList(files)); + } + + private static JavaSource create(final ClasspathInfo cpInfo, final PositionConverter binding, final Collection files) throws IllegalArgumentException { + try { + return new JavaSource(cpInfo, binding, files); + } catch (DataObjectNotFoundException donf) { + Logger.getLogger("global").warning("Ignoring non existent file: " + FileUtil.getFileDisplayName(donf.getFileObject())); //NOI18N + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + return null; } private static Map> file2JavaSource = new WeakHashMap>(); @@ -341,43 +352,55 @@ if (!fileObject.isValid()) { return null; } - if ("text/x-java".equals(FileUtil.getMIMEType(fileObject)) || "java".equals(fileObject.getExt())) { //NOI18N - Reference ref = file2JavaSource.get(fileObject); - JavaSource js = ref != null ? ref.get() : null; - if (js == null) { - file2JavaSource.put(fileObject, new WeakReference(js = create(ClasspathInfo.create(fileObject), fileObject))); - } - return js; - } - if ("application/x-class-file".equals(FileUtil.getMIMEType(fileObject)) || "class".equals(fileObject.getExt())) { //NOI18N - ClassPath bootPath = ClassPath.getClassPath(fileObject, ClassPath.BOOT); - ClassPath compilePath = ClassPath.getClassPath(fileObject, ClassPath.COMPILE); - if (compilePath == null) { - compilePath = ClassPathSupport.createClassPath(new URL[0]); - } - ClassPath srcPath = ClassPath.getClassPath(fileObject, ClassPath.SOURCE); - if (srcPath == null) { - srcPath = ClassPathSupport.createClassPath(new URL[0]); - } - ClassPath execPath = ClassPath.getClassPath(fileObject, ClassPath.EXECUTE); - if (execPath != null) { - bootPath = ClassPathSupport.createProxyClassPath(execPath, bootPath); + + Reference ref = file2JavaSource.get(fileObject); + JavaSource js = ref != null ? ref.get() : null; + if (js == null) { + if ("application/x-class-file".equals(FileUtil.getMIMEType(fileObject)) || "class".equals(fileObject.getExt())) { //NOI18N + ClassPath bootPath = ClassPath.getClassPath(fileObject, ClassPath.BOOT); + ClassPath compilePath = ClassPath.getClassPath(fileObject, ClassPath.COMPILE); + if (compilePath == null) { + compilePath = ClassPathSupport.createClassPath(new URL[0]); + } + ClassPath srcPath = ClassPath.getClassPath(fileObject, ClassPath.SOURCE); + if (srcPath == null) { + srcPath = ClassPathSupport.createClassPath(new URL[0]); + } + ClassPath execPath = ClassPath.getClassPath(fileObject, ClassPath.EXECUTE); + if (execPath != null) { + bootPath = ClassPathSupport.createProxyClassPath(execPath, bootPath); + } + final ClasspathInfo info = ClasspathInfo.create(bootPath, compilePath, srcPath); + FileObject root = ClassPathSupport.createProxyClassPath(bootPath,compilePath,srcPath).findOwnerRoot(fileObject); + try { + js = new JavaSource (info,fileObject,root); + } catch (IOException ioe) { + Exceptions.printStackTrace(ioe); + } + } else { + PositionConverter binding = null; + if (!"text/x-java".equals(FileUtil.getMIMEType(fileObject)) && !"java".equals(fileObject.getExt())) { //NOI18N + for (JavaSourceProvider provider : Lookup.getDefault().lookupAll(JavaSourceProvider.class)) { + JavaFileFilterImplementation filter = provider.forFileObject(fileObject); + if (filter != null) { + binding = new PositionConverter(fileObject, filter); + break; + } + } + if (binding == null) + return null; } - final ClasspathInfo info = ClasspathInfo.create(bootPath, compilePath, srcPath); - FileObject root = ClassPathSupport.createProxyClassPath(bootPath,compilePath,srcPath).findOwnerRoot(fileObject); - try { - return new JavaSource (info,fileObject,root); - } catch (IOException ioe) { - Exceptions.printStackTrace(ioe); + js = create(ClasspathInfo.create(fileObject), binding, Collections.singletonList(fileObject)); } + file2JavaSource.put(fileObject, new WeakReference(js)); } - return null; + return js; } /** - * Returns a {@link JavaSource} instance associated to {@link org.openide.filesystems.FileObject} - * the {@link Document} was created from, it returns null if the {@link Document} is not - * associanted with data type providing the {@link JavaSource}. + * Returns a {@link JavaSource} instance associated to the given {@link javax.swing.Document}, + * it returns null if the {@link Document} is not + * associated with data type providing the {@link JavaSource}. * @param doc {@link Document} for which the {@link JavaSource} should be found/created. * @return {@link JavaSource} or null * @throws {@link IllegalArgumentException} if doc is null @@ -386,8 +409,7 @@ if (doc == null) { throw new IllegalArgumentException ("doc == null"); //NOI18N } - Reference ref = (Reference) doc.getProperty(JavaSource.class); - JavaSource js = ref != null ? (JavaSource) ref.get() : null; + JavaSource js = (JavaSource)doc.getProperty(JavaSource.class); if (js == null) { DataObject dObj = (DataObject)doc.getProperty(Document.StreamDescriptionProperty); if (dObj != null) @@ -401,10 +423,11 @@ * @param files to create JavaSource for * @param cpInfo classpath info */ - private JavaSource (ClasspathInfo cpInfo, Collection files) throws IOException { + private JavaSource (ClasspathInfo cpInfo, PositionConverter binding, Collection files) throws IOException { this.reparseDelay = REPARSE_DELAY; this.files = Collections.unmodifiableList(new ArrayList(files)); //Create a defensive copy, prevent modification this.fileChangeListener = new FileChangeListenerImpl (); + this.binding = binding; boolean multipleSources = this.files.size() > 1, filterAssigned = false; for (Iterator it = this.files.iterator(); it.hasNext();) { FileObject file = it.next(); @@ -418,7 +441,10 @@ } if (!filterAssigned) { filterAssigned = true; - JavaFileFilterImplementation filter = JavaFileFilterQuery.getFilter(file); + if (this.binding == null) { + this.binding = new PositionConverter(file, JavaFileFilterQuery.getFilter(file)); + } + JavaFileFilterImplementation filter = this.binding.getFilter(); if (filter != null) { this.filterListener = new FilterListener (filter); } @@ -433,7 +459,7 @@ } } } - this.classpathInfo = cpInfo; + this.classpathInfo = cpInfo; if (this.files.size() == 1) { this.rootFo = classpathInfo.getClassPath(PathKind.SOURCE).findOwnerRoot(this.files.iterator().next()); } @@ -486,7 +512,7 @@ assert !holdsDocumentWriteLock(files) : "JavaSource.runCompileControlTask called under Document write lock."; //NOI18N boolean a = false; - assert a = true; + assert a = true; if (a && javax.swing.SwingUtilities.isEventDispatchThread()) { StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[2]; if (warnedAboutRunInEQ.add(stackTraceElement)) { @@ -512,7 +538,7 @@ } } if (currentInfo == null) { - currentInfo = createCurrentInfo(this,this.files.isEmpty() ? null : this.files.iterator().next(), filterListener, null); + currentInfo = createCurrentInfo(this, binding, null); if (shared) { synchronized (this) { if (this.currentInfo == null || (this.flags & INVALID) != 0) { @@ -581,7 +607,7 @@ else { restarted = true; } - CompilationInfo ci = createCurrentInfo(this,activeFile,filterListener,jt); + CompilationInfo ci = createCurrentInfo(this, new PositionConverter(activeFile, null), jt); task.run(new CompilationController(ci)); if (!ci.needsRestart) { jt = ci.getJavacTask(); @@ -732,7 +758,7 @@ } } if (currentInfo == null) { - currentInfo = createCurrentInfo(this,this.files.isEmpty() ? null : this.files.iterator().next(), filterListener, null); + currentInfo = createCurrentInfo(this, binding, null); synchronized (this) { if (this.currentInfo == null || (this.flags & INVALID) != 0) { this.currentInfo = currentInfo; @@ -791,7 +817,7 @@ else { restarted = true; } - CompilationInfo ci = createCurrentInfo(this,activeFile, filterListener, jt); + CompilationInfo ci = createCurrentInfo(this, new PositionConverter(activeFile, null), jt); WorkingCopy copy = new WorkingCopy(ci); task.run(copy); if (!ci.needsRestart) { @@ -864,7 +890,7 @@ currentInfo = this.currentInfo; } if (currentInfo == null) { - currentInfo = createCurrentInfo (this, this.files.isEmpty() ? null : this.files.iterator().next(), filterListener, null); + currentInfo = createCurrentInfo (this, binding, null); } synchronized (this) { if (this.currentInfo == null) { @@ -1407,7 +1433,7 @@ try { //createCurrentInfo has to be out of synchronized block, it aquires an editor lock if (jsInvalid) { - ci = createCurrentInfo (js,js.files.isEmpty() ? null : js.files.iterator().next(), js.filterListener, null); + ci = createCurrentInfo (js, js.binding, null); synchronized (js) { if ((js.flags & INVALID) != 0) { js.currentInfo = ci; @@ -1715,11 +1741,8 @@ private final class FilterListener implements ChangeListener { - private final JavaFileFilterImplementation filter; - public FilterListener (final JavaFileFilterImplementation filter) { - this.filter = filter; - this.filter.addChangeListener(WeakListeners.change(this, this.filter)); + filter.addChangeListener(WeakListeners.change(this, filter)); } public void stateChanged(ChangeEvent event) { @@ -1727,10 +1750,12 @@ } } - private static CompilationInfo createCurrentInfo (final JavaSource js, final FileObject fo, final FilterListener filterListener, final JavacTaskImpl javac) throws IOException { - CompilationInfo info = new CompilationInfo (js, fo, filterListener == null ? null : filterListener.filter, javac); - Logger.getLogger("TIMER").log(Level.FINE, "CompilationInfo", - new Object[] {fo, info}); + private static CompilationInfo createCurrentInfo (final JavaSource js, final PositionConverter binding, final JavacTaskImpl javac) throws IOException { + CompilationInfo info = new CompilationInfo (js, binding, javac); + if (binding != null) { + Logger.getLogger("TIMER").log(Level.FINE, "CompilationInfo", + new Object[] {binding.getFileObject(), info}); + } return info; } @@ -1814,6 +1839,14 @@ public boolean isDispatchThread () { return factory.isDispatchThread(Thread.currentThread()); } + + public JavaSource create(ClasspathInfo cpInfo, PositionConverter binding, Collection files) throws IllegalArgumentException { + return JavaSource.create(cpInfo, binding, files); + } + + public PositionConverter create(FileObject fo, int offset, int length, JTextComponent component) { + return new PositionConverter(fo, offset, length, component); + } } @@ -2179,7 +2212,7 @@ String dumpDir = System.getProperty("netbeans.user") + "/var/log/"; //NOI18N String src = info.getText(); FileObject file = info.getFileObject(); - String fileName = FileUtil.getFileDisplayName(info.getFileObject()); + String fileName = FileUtil.getFileDisplayName(file); String origName = file.getName(); File f = new File(dumpDir + origName + ".dump"); // NOI18N boolean dumpSucceeded = false; Index: source/src/org/netbeans/api/java/source/PositionConverter.java =================================================================== RCS file: source/src/org/netbeans/api/java/source/PositionConverter.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ source/src/org/netbeans/api/java/source/PositionConverter.java 15 Jun 2007 10:57:09 -0000 @@ -0,0 +1,154 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.api.java.source; + +import java.io.Reader; +import java.io.Writer; +import java.util.concurrent.CopyOnWriteArrayList; +import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.JTextComponent; +import org.netbeans.modules.java.preprocessorbridge.spi.JavaFileFilterImplementation; +import org.netbeans.modules.java.preprocessorbridge.spi.JavaSourceProvider; +import org.openide.filesystems.FileObject; + +/**Binding between virtual Java source and the real source. + * Please note that this class is needed only for clients that need to work + * in non-Java files (eg. JSP files) or in dialogs, like code completion. + * Most clients do not need to use this class. + * + * @author Dusan Balek + */ +public final class PositionConverter { + + private FileObject fo; + private JavaFileFilterImplementation filter; + private int offset; + private int length; + private JTextComponent component; + + PositionConverter (final FileObject fo, final JavaFileFilterImplementation filter) { + this.fo = fo; + this.filter = filter; + } + + PositionConverter (final FileObject fo, int offset, int length, final JTextComponent component) { + this.fo = fo; + this.offset = offset; + this.length = length; + this.component = component; + this.filter = new Filter(); + } + // API of the class -------------------------------------------------------- + + /**Compute position in the document for given position in the virtual + * Java source. + * + * @param javaSourcePosition position in the virtual Java Source + * @return position in the document + */ + public int getOriginalPosition(int javaSourcePosition) { + if (filter instanceof JavaSourceProvider.PositionTranslatingJavaFileFilterImplementation) { + return ((JavaSourceProvider.PositionTranslatingJavaFileFilterImplementation)filter).getOriginalPosition(javaSourcePosition); + } + return javaSourcePosition; + } + + /**Compute position in the virtual Java source for given position + * in the document. + * + * @param originalPosition position in the document + * @return position in the virtual Java source + */ + public int getJavaSourcePosition(int originalPosition) { + if (filter instanceof JavaSourceProvider.PositionTranslatingJavaFileFilterImplementation) { + return ((JavaSourceProvider.PositionTranslatingJavaFileFilterImplementation)filter).getJavaSourcePosition(originalPosition); + } + return originalPosition; + } + + // Package private methods ------------------------------------------------- + + JavaFileFilterImplementation getFilter() { + return filter; + } + + FileObject getFileObject() { + return fo; + } + + // Nested classes ---------------------------------------------------------- + + private class Filter implements JavaSourceProvider.PositionTranslatingJavaFileFilterImplementation, DocumentListener { + + CopyOnWriteArrayList listeners = new CopyOnWriteArrayList(); + + public Filter() { + component.getDocument().addDocumentListener(this); + } + + public Reader filterReader(Reader r) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public CharSequence filterCharSequence(CharSequence charSequence) { + return charSequence.subSequence(0, offset) + + component.getText() + + charSequence.subSequence(offset + length, charSequence.length()); + } + + public Writer filterWriter(Writer w) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void addChangeListener(ChangeListener listener) { + listeners.addIfAbsent(listener); + } + + public void removeChangeListener(ChangeListener listener) { + listeners.remove(listener); + } + + public int getOriginalPosition(int javaSourcePosition) { + if (javaSourcePosition < offset) + return -1; + int diff = javaSourcePosition - offset; + return diff <= component.getText().length() ? diff : -1; + } + + public int getJavaSourcePosition(int originalPosition) { + return offset + originalPosition; + } + + public void insertUpdate(DocumentEvent event) { + this.changedUpdate(event); + } + + public void removeUpdate(DocumentEvent event) { + this.changedUpdate(event); + } + + public void changedUpdate(DocumentEvent event) { + for (ChangeListener changeListener : listeners) + changeListener.stateChanged(null); + } + } +} Index: source/src/org/netbeans/api/java/source/SourceUtils.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/api/java/source/SourceUtils.java,v --- source/src/org/netbeans/api/java/source/SourceUtils.java 14 Jun 2007 14:54:52 -0000 1.34 +++ source/src/org/netbeans/api/java/source/SourceUtils.java 15 Jun 2007 10:57:09 -0000 @@ -27,6 +27,7 @@ import java.util.*; import javax.lang.model.element.*; +import javax.lang.model.type.*; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; @@ -59,8 +60,11 @@ import java.net.URLEncoder; import javax.swing.text.BadLocationException; import javax.swing.text.Document; +import java.util.logging.Level; +import java.util.logging.Logger; import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.lexer.JavaTokenId; import org.netbeans.api.java.classpath.GlobalPathRegistry; import org.netbeans.api.java.queries.JavadocForBinaryQuery; import org.netbeans.api.java.queries.SourceForBinaryQuery; @@ -68,6 +72,9 @@ import org.netbeans.api.java.source.ClasspathInfo.PathKind; import org.netbeans.api.java.source.JavaSource.Phase; import org.netbeans.api.java.source.WorkingCopy; +import org.netbeans.api.lexer.TokenHierarchy; +import org.netbeans.api.lexer.TokenId; +import org.netbeans.api.lexer.TokenSequence; import org.netbeans.modules.java.JavaDataLoader; import org.netbeans.modules.java.source.parsing.FileObjects; import org.netbeans.modules.java.source.pretty.VeryPretty; @@ -79,8 +86,10 @@ import org.openide.filesystems.FileObject; import org.openide.filesystems.FileStateInvalidException; +import org.openide.filesystems.FileUtil; import org.openide.filesystems.URLMapper; import org.openide.util.Exceptions; +import org.openide.util.Parameters; import org.openide.util.RequestProcessor; /** @@ -124,6 +133,21 @@ return ((MethodSymbol)method).implementation((TypeSymbol)origin, com.sun.tools.javac.code.Types.instance(c), true); } + public static TokenSequence getJavaTokenSequence(final TokenHierarchy hierarchy, final int offset) { + if (hierarchy != null) { + TokenSequence ts = hierarchy.tokenSequence(); + while(ts != null && (ts.moveNext() || offset == 0)) { + ts.move(offset); + if (!ts.moveNext()) + return null; + if (ts.language() == JavaTokenId.language()) + return (TokenSequence)ts; + ts = ts.embedded(); + } + } + return null; + } + public static boolean checkTypesAssignable(CompilationInfo info, TypeMirror from, TypeMirror to) { Context c = ((JavacTaskImpl) info.getJavacTask()).getContext(); if (from.getKind() == TypeKind.DECLARED) { Index: source/src/org/netbeans/api/java/source/support/CaretAwareJavaSourceTaskFactory.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/api/java/source/support/CaretAwareJavaSourceTaskFactory.java,v --- source/src/org/netbeans/api/java/source/support/CaretAwareJavaSourceTaskFactory.java 2 Nov 2006 10:13:40 -0000 1.3 +++ source/src/org/netbeans/api/java/source/support/CaretAwareJavaSourceTaskFactory.java 15 Jun 2007 10:57:09 -0000 @@ -19,6 +19,7 @@ package org.netbeans.api.java.source.support; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,6 +32,7 @@ import org.netbeans.api.java.source.JavaSource.Phase; import org.netbeans.api.java.source.JavaSource.Priority; import org.netbeans.api.java.source.JavaSourceTaskFactory; +import org.netbeans.api.java.source.SourceUtils; import org.openide.filesystems.FileObject; import org.openide.util.RequestProcessor; @@ -39,6 +41,9 @@ * opened and visible JTextComponents and reschedules the tasks as necessary. * * The tasks may access current caret position using {@link #getLastPosition} method. + * + * The files returned from the {@link #getFileObjects} method will be filtered using + * {@link org.netbeans.api.java.source.SourceUtils#filterSupportedMIMETypes}. * * @author Jan Lahoda */ @@ -48,6 +53,7 @@ private static final RequestProcessor WORKER = new RequestProcessor("CaretAwareJavaSourceTaskFactory worker"); private int timeout; + private String[] supportedMimeTypes; /**Construct the CaretAwareJavaSourceTaskFactory with given {@link Phase} and {@link Priority}. * @@ -55,21 +61,32 @@ * @param priority priority to use for tasks created by {@link #createTask} */ public CaretAwareJavaSourceTaskFactory(Phase phase, Priority priority) { + this(phase, priority, (String[]) null); + } + + /**Construct the CaretAwareJavaSourceTaskFactory with given {@link Phase} and {@link Priority}. + * + * @param phase phase to use for tasks created by {@link #createTask} + * @param priority priority to use for tasks created by {@link #createTask} + * @param supportedMimeTypes a list of mime types on which the tasks created by this factory should be run + */ + public CaretAwareJavaSourceTaskFactory(Phase phase, Priority priority, String... supportedMimeTypes) { super(phase, priority); //XXX: weak, or something like this: OpenedEditors.getDefault().addChangeListener(new ChangeListenerImpl()); this.timeout = DEFAULT_RESCHEDULE_TIMEOUT; + this.supportedMimeTypes = supportedMimeTypes != null ? supportedMimeTypes.clone() : null; } /**@inheritDoc*/ public List getFileObjects() { - List files = new ArrayList(OpenedEditors.getDefault().getVisibleEditorsFiles()); + List files = OpenedEditors.filterSupportedMIMETypes(OpenedEditors.getDefault().getVisibleEditorsFiles(), supportedMimeTypes); return files; } - private Map component2Listener = new HashMap(); - private static Map file2LastPosition = new WeakHashMap(); + private Map component2Listener = new HashMap(); + private static Map file2LastPosition = new WeakHashMap(); /**Returns current caret position in current {@link JTextComponent} for a given file. * Index: source/src/org/netbeans/api/java/source/support/EditorAwareJavaSourceTaskFactory.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/api/java/source/support/EditorAwareJavaSourceTaskFactory.java,v --- source/src/org/netbeans/api/java/source/support/EditorAwareJavaSourceTaskFactory.java 2 Nov 2006 10:13:41 -0000 1.3 +++ source/src/org/netbeans/api/java/source/support/EditorAwareJavaSourceTaskFactory.java 15 Jun 2007 10:57:09 -0000 @@ -18,39 +18,52 @@ */ package org.netbeans.api.java.source.support; -import java.util.ArrayList; import java.util.List; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import javax.swing.text.Document; -import javax.swing.text.JTextComponent; import org.netbeans.api.java.source.JavaSource.Phase; import org.netbeans.api.java.source.JavaSource.Priority; import org.netbeans.api.java.source.JavaSourceTaskFactory; +import org.netbeans.api.java.source.SourceUtils; import org.openide.filesystems.FileObject; -import org.openide.loaders.DataObject; /**A {@link JavaSourceTaskFactorySupport} that registers tasks to all files that are * opened in the editor and are visible. * + * The files returned from the {@link #getFileObjects} method will be filtered using + * {@link org.netbeans.api.java.source.SourceUtils#filterSupportedMIMETypes}. + * * @author Jan Lahoda */ public abstract class EditorAwareJavaSourceTaskFactory extends JavaSourceTaskFactory { + private String[] supportedMimeTypes; + /**Construct the EditorAwareJavaSourceTaskFactory with given {@link Phase} and {@link Priority}. * * @param phase phase to use for tasks created by {@link #createTask} * @param priority priority to use for tasks created by {@link #createTask} */ protected EditorAwareJavaSourceTaskFactory(Phase phase, Priority priority) { + this(phase, priority, (String[]) null); + } + + /**Construct the EditorAwareJavaSourceTaskFactory with given {@link Phase} and {@link Priority}. + * + * @param phase phase to use for tasks created by {@link #createTask} + * @param priority priority to use for tasks created by {@link #createTask} + * @param supportedMimeTypes a list of mime types on which the tasks created by this factory should be run + */ + protected EditorAwareJavaSourceTaskFactory(Phase phase, Priority priority, String... supportedMimeTypes) { super(phase, priority); //XXX: weak, or something like this: OpenedEditors.getDefault().addChangeListener(new ChangeListenerImpl()); + this.supportedMimeTypes = supportedMimeTypes != null ? supportedMimeTypes.clone() : null; } /**@inheritDoc*/ public List getFileObjects() { - List files = new ArrayList(OpenedEditors.getDefault().getVisibleEditorsFiles()); + List files = OpenedEditors.filterSupportedMIMETypes(OpenedEditors.getDefault().getVisibleEditorsFiles(), supportedMimeTypes); return files; } Index: source/src/org/netbeans/api/java/source/support/LookupBasedJavaSourceTaskFactory.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/api/java/source/support/LookupBasedJavaSourceTaskFactory.java,v --- source/src/org/netbeans/api/java/source/support/LookupBasedJavaSourceTaskFactory.java 2 Nov 2006 10:13:40 -0000 1.3 +++ source/src/org/netbeans/api/java/source/support/LookupBasedJavaSourceTaskFactory.java 15 Jun 2007 10:57:09 -0000 @@ -21,12 +21,14 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.swing.event.ChangeEvent; import org.netbeans.api.java.source.JavaSource.Phase; import org.netbeans.api.java.source.JavaSource.Priority; import org.netbeans.api.java.source.JavaSourceTaskFactory; +import org.netbeans.api.java.source.SourceUtils; import org.openide.filesystems.FileObject; import org.openide.loaders.DataObject; import org.openide.nodes.Node; @@ -42,6 +44,9 @@ * in the lookup. If {@link Node}(s) are found, its/their lookup is searched for * {@link FileObject} and {@link DataObject}. * + * The files returned from the {@link #getFileObjects} method will be filtered using + * {@link org.netbeans.api.java.source.SourceUtils#filterSupportedMIMETypes}. + * * @author Jan Lahoda */ public abstract class LookupBasedJavaSourceTaskFactory extends JavaSourceTaskFactory { @@ -52,6 +57,8 @@ private List currentFiles; private LookupListener listener; + + private String[] supportedMimeTypes; /**Construct the LookupBasedJavaSourceTaskFactory with given {@link Phase} and {@link Priority}. * @@ -59,9 +66,19 @@ * @param priority priority to use for tasks created by {@link #createTask} */ public LookupBasedJavaSourceTaskFactory(Phase phase, Priority priority) { + this(phase, priority, (String[]) null); + } + + /**Construct the LookupBasedJavaSourceTaskFactory with given {@link Phase} and {@link Priority}. + * + * @param phase phase to use for tasks created by {@link #createTask} + * @param priority priority to use for tasks created by {@link #createTask} + */ + public LookupBasedJavaSourceTaskFactory(Phase phase, Priority priority, String... supportedMimeTypes) { super(phase, priority); currentFiles = Collections.emptyList(); listener = new LookupListenerImpl(); + this.supportedMimeTypes = supportedMimeTypes != null ? supportedMimeTypes.clone() : null; } /**Sets a new {@link Lookup} to search. @@ -91,7 +108,7 @@ } private synchronized void updateCurrentFiles() { - Set newCurrentFiles = new HashSet(); + Set newCurrentFiles = new HashSet(); newCurrentFiles.addAll(fileObjectResult.allInstances()); @@ -107,7 +124,7 @@ } } - currentFiles = new ArrayList(newCurrentFiles); + currentFiles = OpenedEditors.filterSupportedMIMETypes(new LinkedList(newCurrentFiles), supportedMimeTypes); lookupContentChanged(); } Index: source/src/org/netbeans/api/java/source/support/OpenedEditors.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/api/java/source/support/OpenedEditors.java,v --- source/src/org/netbeans/api/java/source/support/OpenedEditors.java 2 Nov 2006 10:13:41 -0000 1.3 +++ source/src/org/netbeans/api/java/source/support/OpenedEditors.java 15 Jun 2007 10:57:09 -0000 @@ -21,19 +21,26 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.JEditorPane; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.Document; import javax.swing.text.JTextComponent; +import org.netbeans.api.java.source.JavaSource; import org.netbeans.editor.Registry; import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; import org.openide.loaders.DataObject; +import org.openide.util.Parameters; /** * @@ -98,7 +105,8 @@ JTextComponent editor = Registry.getMostActiveComponent(); - if (editor instanceof JEditorPane && "text/x-java".equals(((JEditorPane) editor).getContentType())) { + FileObject fo = editor != null ? getFileObject(editor) : null; + if (editor instanceof JEditorPane && fo != null && JavaSource.forFileObject(fo) != null) { visibleEditors.add(editor); } @@ -131,4 +139,103 @@ return null; } + /**Checks if the given file is supported. See {@link #filterSupportedMIMETypes} + * for more details. + * + * @param file to check + * @param type the type to check for the {@link SupportedMimeTypes} annotation + * @return true if and only if the given file is supported (see {@link #filterSupportedMIMETypes}) + * @throws NullPointerException if file == null or type == null + */ + public static boolean isSupported(FileObject file, String... mimeTypes) throws NullPointerException { + Parameters.notNull("files", file); + + return !filterSupportedMIMETypes(Collections.singletonList(file), mimeTypes).isEmpty(); + } + + /**Filter unsupported files from the files parameter. A supported file + * f is defined as follows: + *
    + *
  • JavaSource.forFileObject(f) != null
  • + *
  • If the type is annotated with the {@link SupportedMimeTypes} annotation, + * the file is supported if type.getAnnotation(SupportedMimeTypes.class).value() + * contains FileUtil.getMIMEType(f). + *
  • + *
  • If the type is not annotated with the {@link SupportedMimeTypes} annotation, + * the file is supported if FileUtil.getMIMEType(f) == "text/x-java". + *
+ * + * @param files the list of files to filter + * @param type the type to check for the {@link SupportedMimeTypes} annotation + * @return list of files that are supported (see above). + * @throws NullPointerException if files == null or type == null + */ + public static List filterSupportedMIMETypes(Collection files, String... mimeTypes) throws NullPointerException { + Parameters.notNull("files", files); + + boolean allowJavaExtension = false; + + if (mimeTypes == null) { + mimeTypes = new String[] {"text/x-java"}; + allowJavaExtension = true; + } + + List mimeTypesList = Arrays.asList(mimeTypes); + boolean allowAll = mimeTypesList.contains("*"); + List result = new LinkedList(); + + Logger.getLogger(OpenedEditors.class.getName()).log(Level.FINER, "mimeTypesList={0}", mimeTypesList); + + for (FileObject f : files) { + Logger.getLogger(OpenedEditors.class.getName()).log(Level.FINER, "analyzing={0}", f); + + if (JavaSource.forFileObject(f) == null) + continue; + + if (allowAll) { + result.add(f); + continue; + } + + if (allowJavaExtension && "java".equals(f.getExt())) { + result.add(f); + continue; + } + + String fileMimeType = FileUtil.getMIMEType(f); + + Logger.getLogger(OpenedEditors.class.getName()).log(Level.FINER, "fileMimeType={0}", fileMimeType); + + if (mimeTypesList.contains(fileMimeType)) { + result.add(f); + continue; + } + + String shorterMimeType = fileMimeType; + + while (true) { + int slash = shorterMimeType.indexOf('/'); + + if (slash == (-1)) + break; + + int plus = shorterMimeType.indexOf('+', slash); + + if (plus == (-1)) + break; + + shorterMimeType = shorterMimeType.substring(0, slash + 1) + shorterMimeType.substring(plus + 1); + + if (mimeTypesList.contains(shorterMimeType)) { + result.add(f); + break; + } + } + } + + Logger.getLogger(OpenedEditors.class.getName()).log(Level.FINE, "filter({0}, {1})={2}", new Object[] {files, mimeTypesList, result}); + + return result; + } + } Index: source/src/org/netbeans/modules/java/source/JavaSourceAccessor.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/modules/java/source/JavaSourceAccessor.java,v --- source/src/org/netbeans/modules/java/source/JavaSourceAccessor.java 24 May 2007 14:16:07 -0000 1.10 +++ source/src/org/netbeans/modules/java/source/JavaSourceAccessor.java 15 Jun 2007 10:57:09 -0000 @@ -21,15 +21,19 @@ import com.sun.tools.javac.api.JavacTaskImpl; import java.io.IOException; +import java.util.Collection; +import javax.swing.text.JTextComponent; import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; import org.netbeans.api.java.source.CancellableTask; import org.netbeans.api.java.source.ClasspathInfo; import org.netbeans.api.java.source.CompilationInfo; import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.PositionConverter; import org.netbeans.api.java.source.WorkingCopy; import org.netbeans.modules.java.source.builder.DefaultEnvironment; import org.openide.ErrorManager; +import org.openide.filesystems.FileObject; /** * @@ -85,6 +89,9 @@ public abstract void revalidate(JavaSource js); + public abstract JavaSource create(final ClasspathInfo cpInfo, final PositionConverter binding, final Collection files) throws IllegalArgumentException; + + public abstract PositionConverter create(final FileObject fo, int offset, int length, final JTextComponent component); /** * Returns true when the caller is a {@link JavaSource} worker thread Index: source/src/org/netbeans/modules/java/source/parsing/SourceFileObject.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/modules/java/source/parsing/SourceFileObject.java,v --- source/src/org/netbeans/modules/java/source/parsing/SourceFileObject.java 5 Jun 2007 13:08:55 -0000 1.12 +++ source/src/org/netbeans/modules/java/source/parsing/SourceFileObject.java 15 Jun 2007 10:57:09 -0000 @@ -366,9 +366,13 @@ doc.render(new Runnable() { public void run () { try { - int len = doc.getLength(); + CharSequence text = doc.getText(0, doc.getLength()); + if (filter != null) { + text = filter.filterCharSequence(text); + } + int len = text.length(); result[0] = new char[len+1]; - doc.getText(0,len).getChars(0,len,result[0],0); + text.toString().getChars(0,len,result[0],0); length[0] = len; } catch (BadLocationException e) { ErrorManager.getDefault().notify(e); Index: source/test/unit/src/org/netbeans/api/java/source/JavaSourceTest.java =================================================================== RCS file: /cvs/java/source/test/unit/src/org/netbeans/api/java/source/JavaSourceTest.java,v --- source/test/unit/src/org/netbeans/api/java/source/JavaSourceTest.java 14 Jun 2007 10:41:05 -0000 1.14 +++ source/test/unit/src/org/netbeans/api/java/source/JavaSourceTest.java 15 Jun 2007 10:57:09 -0000 @@ -1119,7 +1119,7 @@ js.runUserActionTask(new CancellableTask() { public void run(CompilationController cc) throws IOException { - files.add(cc.getFileObject()); + files.add(cc.getPositionConverter().getFileObject()); cc.toPhase(Phase.RESOLVED); } public void cancel() {} @@ -1131,7 +1131,7 @@ js.runModificationTask(new CancellableTask() { public void run(WorkingCopy cc) throws IOException { - files.add(cc.getFileObject()); + files.add(cc.getPositionConverter().getFileObject()); cc.toPhase(Phase.RESOLVED); } public void cancel() {} Index: source/test/unit/src/org/netbeans/api/java/source/SourceUtilsTest.java =================================================================== RCS file: /cvs/java/source/test/unit/src/org/netbeans/api/java/source/SourceUtilsTest.java,v --- source/test/unit/src/org/netbeans/api/java/source/SourceUtilsTest.java 16 Apr 2007 15:54:20 -0000 1.4 +++ source/test/unit/src/org/netbeans/api/java/source/SourceUtilsTest.java 15 Jun 2007 10:57:09 -0000 @@ -21,6 +21,8 @@ import java.io.File; import java.io.IOException; +import java.io.Reader; +import java.io.Writer; import java.net.URL; import java.util.Arrays; import java.util.HashMap; @@ -39,7 +41,10 @@ import org.netbeans.api.java.queries.SourceForBinaryQuery.Result; import org.netbeans.api.java.source.ClasspathInfo; import org.netbeans.api.java.source.ClasspathInfo.PathKind; +import org.netbeans.api.java.source.JavaSource.Phase; +import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.java.preprocessorbridge.spi.JavaSourceProvider; import org.netbeans.modules.java.source.ElementHandleAccessor; import org.netbeans.modules.java.source.TestUtil; import org.netbeans.modules.java.source.usages.ClasspathInfoAccessor; @@ -48,6 +53,8 @@ import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; +import org.openide.filesystems.MIMEResolver; +import org.openide.util.Lookup; /** * @@ -422,4 +429,98 @@ } } +// //tests for SourceUtils.filterSupportedMIMETypes: +// +// @SuppressWarnings("deprecation") +// public void testFilter() throws Exception { +// SourceUtilsTestUtil.setLookup(new Object[] {new JavaSourceProviderImpl(), new ResolverImpl()}, SourceUtilsTest.class.getClassLoader()); +// boolean registered = false; +// +// for (JavaSourceProvider p : Lookup.getDefault().lookupAll(JavaSourceProvider.class)) { +// if (p instanceof JavaSourceProvider) { +// registered = true; +// break; +// } +// } +// +// assertTrue(registered); +// +// FileObject work = FileUtil.toFileObject(getWorkDir()); +// +// FileObject file1 = FileUtil.createData(work, "test.ext1"); +// FileObject file2 = FileUtil.createData(work, "test.ext2"); +// FileObject file3 = FileUtil.createData(work, "test.ext3"); +// FileObject file4 = FileUtil.createData(work, "test.ext4"); +// FileObject file5 = FileUtil.createData(work, "test.txt"); +// FileObject file6 = FileUtil.createData(work, "test.ant"); +// +// assertEquals("text/x-java", FileUtil.getMIMEType(file1)); +// assertEquals("text/jsp", FileUtil.getMIMEType(file2)); +// assertEquals("text/plain", FileUtil.getMIMEType(file3)); +// assertEquals("text/test+x-java", FileUtil.getMIMEType(file4)); +// assertEquals("text/test+x-ant+xml", FileUtil.getMIMEType(file6)); +// +// List files = Arrays.asList(file1, file2, file3, file4, file5, file6); +// +// assertEquals(Arrays.asList(file1, file2, file3, file4, file6), SourceUtils.filterSupportedMIMETypes(files, F1.class)); +// assertEquals(Arrays.asList(file1, file4), SourceUtils.filterSupportedMIMETypes(files, F2.class)); +// assertEquals(Arrays.asList(file4), SourceUtils.filterSupportedMIMETypes(files, F3.class)); +// assertEquals(Arrays.asList(file1, file4), SourceUtils.filterSupportedMIMETypes(files, F4.class)); +// assertEquals(Arrays.asList(file1, file2, file4), SourceUtils.filterSupportedMIMETypes(files, F5.class)); +// } +// +// public static class JavaSourceProviderImpl implements JavaSourceProvider { +// public PositionTranslatingJavaFileFilterImplementation forFileObject(FileObject fo) { +// if ("txt".equals(fo.getExt())) +// return null; +// +// return new PositionTranslatingJavaFileFilterImplementation() { +// public int getOriginalPosition(int javaSourcePosition) { +// return javaSourcePosition; +// } +// public int getJavaSourcePosition(int originalPosition) { +// return originalPosition; +// } +// public Reader filterReader(Reader r) { +// return r; +// } +// public CharSequence filterCharSequence(CharSequence charSequence) { +// return charSequence; +// } +// public Writer filterWriter(Writer w) { +// return w; +// } +// public void addChangeListener(ChangeListener listener) {} +// public void removeChangeListener(ChangeListener listener) {} +// }; +// } +// } +// +// public static class ResolverImpl extends MIMEResolver { +// public String findMIMEType(FileObject fo) { +// String ext = fo.getExt(); +// +// if ("ext1".contains(ext)) return "text/x-java"; +// if ("ext2".contains(ext)) return "text/jsp"; +// if ("ext3".contains(ext)) return "text/plain"; +// if ("ext4".contains(ext)) return "text/test+x-java"; +// if ("ant".contains(ext)) return "text/test+x-ant+xml"; +// +// return null; +// } +// } +// +// @SupportedMimeTypes("*") +// private static class F1 {} +// +// @SupportedMimeTypes("text/x-java") +// private static class F2 {} +// +// @SupportedMimeTypes("text/test+x-java") +// private static class F3 {} +// +// private static class F4 {} +// +// @SupportedMimeTypes({"text/x-java", "text/jsp"}) +// private static class F5 {} } Index: sourceui/nbproject/project.xml =================================================================== RCS file: /cvs/java/sourceui/nbproject/project.xml,v --- sourceui/nbproject/project.xml 14 Jun 2007 22:06:52 -0000 1.2 +++ sourceui/nbproject/project.xml 15 Jun 2007 10:57:09 -0000 @@ -6,6 +6,15 @@ org.netbeans.modules.java.sourceui + org.netbeans.api.java + + + + 1 + 1.13 + + + org.netbeans.libs.javacapi @@ -14,6 +23,15 @@ + org.netbeans.modules.java.lexer + + + + 1 + 1.2 + + + org.netbeans.modules.java.source @@ -22,16 +40,25 @@ - org.netbeans.modules.projectapi + org.netbeans.modules.jumpto 1 - 1.13 + 1.2 - org.netbeans.api.java + org.netbeans.modules.lexer + + + + 2 + 1.20 + + + + org.netbeans.modules.projectapi @@ -77,15 +104,6 @@ 7.9 - - - - org.netbeans.modules.jumpto - - - - 1 - 1.2 Index: sourceui/src/org/netbeans/api/java/source/ui/DialogBinding.java =================================================================== RCS file: sourceui/src/org/netbeans/api/java/source/ui/DialogBinding.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sourceui/src/org/netbeans/api/java/source/ui/DialogBinding.java 15 Jun 2007 10:57:09 -0000 @@ -0,0 +1,76 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Portions Copyrighted 2007 Sun Microsystems, Inc. + */ +package org.netbeans.api.java.source.ui; + +import java.util.Collections; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.api.java.lexer.JavaTokenId; +import org.netbeans.api.java.source.ClasspathInfo; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.PositionConverter; +import org.netbeans.api.lexer.Language; +import org.netbeans.modules.java.source.JavaSourceAccessor; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; + +/** + * + * @author Dusan Balek, Jan Lahoda + */ +public final class DialogBinding { + + private DialogBinding() {} + + /** + * Bind given component and given file together. + * @param fileObject to bind + * @param offset position at which content of the component will be virtually placed + * @param length how many characters replace from the original file + * @param component component to bind + * @return {@link JavaSource} or null + * @throws {@link IllegalArgumentException} if fileObject is null + */ + public static JavaSource bindComponentToFile(FileObject fileObject, int offset, int length, JTextComponent component) throws IllegalArgumentException { + if (fileObject == null) { + throw new IllegalArgumentException ("fileObject == null"); //NOI18N + } + if (!fileObject.isValid()) { + return null; + } + if (!"text/x-java".equals(FileUtil.getMIMEType(fileObject)) && !"java".equals(fileObject.getExt())) { //NOI18N + //TODO: JavaSource cannot be created for all kinds of files, but text/x-java is too restrictive: + return null; + } + Document doc = component.getDocument(); + + if (doc.getProperty(JavaSource.class) != null) { + throw new IllegalArgumentException("A JavaSource is already attached to the given component."); + } + + JavaSource js = JavaSourceAccessor.INSTANCE.create(ClasspathInfo.create(fileObject), JavaSourceAccessor.INSTANCE.create(fileObject, offset, length, component), Collections.singletonList(fileObject)); + + doc.putProperty(JavaSource.class, js); + + if (doc.getProperty(Language.class) == null) { + doc.putProperty(Language.class, JavaTokenId.language()); + } + + return js; + } + +}