diff --git a/api.java/apichanges.xml b/api.java/apichanges.xml --- a/api.java/apichanges.xml +++ b/api.java/apichanges.xml @@ -76,6 +76,25 @@ + + + Added SourceJavadocAttacher to allow clients to attach source (javadoc) roots to binary root + + + + + +

+ Added an API to allow clients to attach source roots and javadoc roots to binary roots. + The API delegates to SPI implementations which provide specific behavior depending on + type of binary root (platform, library, maven artifact). There is also fallback implementation + handling unknown binary roots by storing the bindings into IDE's userdir. +

+
+ + + +
Added notifications about source level changes into SourceLevelQuery diff --git a/api.java/manifest.mf b/api.java/manifest.mf --- a/api.java/manifest.mf +++ b/api.java/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.api.java/1 -OpenIDE-Module-Specification-Version: 1.34 +OpenIDE-Module-Specification-Version: 1.35 OpenIDE-Module-Localizing-Bundle: org/netbeans/api/java/queries/Bundle.properties AutoUpdate-Show-In-Client: false diff --git a/api.java/src/org/netbeans/api/java/queries/SourceJavadocAttacher.java b/api.java/src/org/netbeans/api/java/queries/SourceJavadocAttacher.java new file mode 100644 --- /dev/null +++ b/api.java/src/org/netbeans/api/java/queries/SourceJavadocAttacher.java @@ -0,0 +1,106 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.api.java.queries; + +import java.io.IOException; +import java.net.URL; +import javax.swing.SwingUtilities; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.spi.java.queries.SourceJavadocAttacherImplementation; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; + +/** + * A support for attaching source roots and javadoc roots to binary roots. + * @author Tomas Zezula + * @since 1.35 + */ +public final class SourceJavadocAttacher { + + private SourceJavadocAttacher() {} + + /** + * Attaches a source root provided by the SPI {@link SourceJavadocAttacherImplementation} + * to given binary root. + * Has to be called by event dispatch thread as the SPI implementation may need to show + * an UI to select source root(s). + * @param root the binary root to attach sources to + * @return true if the source root was successfully attached + */ + public static boolean attachSources(@NonNull final URL root) { + return attach(root,0); + } + + /** + * Attaches a javadoc root provided by the SPI {@link SourceJavadocAttacherImplementation} + * to given binary root. + * Has to be called by event dispatch thread as the SPI implementation may need to show + * an UI to select javadoc root(s). + * @param root the binary root to attach javadoc to + * @return true if the javadoc root was successfully attached + */ + public static boolean attachJavadoc(@NonNull final URL root) { + return attach(root,1); + } + + private static boolean attach(final URL root, final int mode) { + if (!SwingUtilities.isEventDispatchThread()) { + throw new IllegalStateException("Has to be called by EDT."); //NOI18N + } + try { + for (SourceJavadocAttacherImplementation attacher : Lookup.getDefault().lookupAll(SourceJavadocAttacherImplementation.class)) { + final SourceJavadocAttacherImplementation.Result res = + mode == 0 ? + attacher.attachSources(root) : + attacher.attachJavadoc(root); + if (res == SourceJavadocAttacherImplementation.Result.ATTACHED) { + return true; + } else if (res == SourceJavadocAttacherImplementation.Result.CANCELED) { + return false; + } + } + } catch (IOException ioe) { + Exceptions.printStackTrace(ioe); + } + return false; + } +} diff --git a/api.java/src/org/netbeans/spi/java/queries/SourceJavadocAttacherImplementation.java b/api.java/src/org/netbeans/spi/java/queries/SourceJavadocAttacherImplementation.java new file mode 100644 --- /dev/null +++ b/api.java/src/org/netbeans/spi/java/queries/SourceJavadocAttacherImplementation.java @@ -0,0 +1,106 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.spi.java.queries; + +import java.io.IOException; +import java.net.URL; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.java.queries.SourceJavadocAttacher; +import org.openide.util.Lookup; +import org.openide.util.lookup.ServiceProvider; + + +/** + * A SPI for attaching source roots and javadoc roots to binary roots. + * The implementations of this interface are registered in global {@link Lookup} + * @see ServiceProvider + * @since 1.35 + * @author Tomas Zezula + * @since 1.35 + */ +public interface SourceJavadocAttacherImplementation { + + /** + * Result of the attaching sources (javadoc) to binary root. + */ + enum Result { + /** + * The source (javadoc) root was successfully attached to + * the binary root. + */ + ATTACHED, + + /** + * User canceled the attaching, no other SPI provider + * is called and {@link SourceJavadocAttacher} returns false. + */ + CANCELED, + + /** + * The SPI is not able to attach sources (javadoc) to given + * binary root (it does not handle it), next SPI provider is + * called. + */ + UNSUPPORTED + } + + + /** + * Attaches a source root provided by this SPI to given binary root. + * Called by event dispatch thread, it's safe to show an UI to select source root(s). + * @param root the binary root to attach sources to + * @return {@link SourceJavadocAttacherImplementation.Result} the result of + * attach operation. + */ + @NonNull + Result attachSources(@NonNull URL root) throws IOException; + + /** + * Attaches a javadoc root provided by this SPI to given binary root. + * Called by event dispatch thread, it's safe to show an UI to select javadoc root(s). + * @param root the binary root to attach javadoc to + * @return {@link SourceJavadocAttacherImplementation.Result} the result of + * attach operation. + */ + @NonNull + Result attachJavadoc(@NonNull URL root) throws IOException; +} diff --git a/java.j2seplatform/nbproject/project.xml b/java.j2seplatform/nbproject/project.xml --- a/java.j2seplatform/nbproject/project.xml +++ b/java.j2seplatform/nbproject/project.xml @@ -59,12 +59,21 @@ + org.netbeans.api.annotations.common + + + + 1 + 1.10 + + + org.netbeans.api.java 1 - 1.18 + 1.35 diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SELibrarySourceJavadocAttacher.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SELibrarySourceJavadocAttacher.java new file mode 100644 --- /dev/null +++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SELibrarySourceJavadocAttacher.java @@ -0,0 +1,159 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.j2seplatform.libraries; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.project.libraries.Library; +import org.netbeans.api.project.libraries.LibraryManager; +import org.netbeans.spi.java.queries.SourceJavadocAttacherImplementation; +import org.openide.util.Exceptions; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author Tomas Zezula + */ +@ServiceProvider(service=SourceJavadocAttacherImplementation.class, position=151) +public class J2SELibrarySourceJavadocAttacher implements SourceJavadocAttacherImplementation { + + @Override + public Result attachSources(@NonNull final URL root) throws IOException { + return attach(root, J2SELibraryTypeProvider.VOLUME_TYPE_SRC); + } + + @Override + public Result attachJavadoc(@NonNull final URL root) throws IOException { + return attach(root, J2SELibraryTypeProvider.VOLUME_TYPE_JAVADOC); + } + + private Result attach( + @NonNull final URL root, + final String volume) { + final Pair pair = findOwner(root); + if (pair == null) { + return Result.UNSUPPORTED; + } + final LibraryManager lm = pair.first; + final Library lib = pair.second; + assert lm != null; + assert lib != null; + try { + final URL areaLocation = lm.getLocation(); + final File baseFolder = areaLocation == null ? null : new File(areaLocation.toURI()).getParentFile(); + final URI[] uris = J2SEVolumeCustomizer.select( + volume, + lib.getName(), + new File[1], + null, + baseFolder); + if (uris != null) { + final String name = lib.getName(); + final String displayName = lib.getDisplayName(); + final String desc = lib.getDescription(); + final Map> volumes = new HashMap>(); + for (String currentVolume : J2SELibraryTypeProvider.VOLUME_TYPES) { + List content = lib.getURIContent(currentVolume); + if (volume == currentVolume) { + final List newContent = new ArrayList(content.size()+uris.length); + newContent.addAll(content); + newContent.addAll(Arrays.asList(uris)); + content = newContent; + } + volumes.put(currentVolume,content); + } + lm.removeLibrary(lib); + lm.createURILibrary( + J2SELibraryTypeProvider.LIBRARY_TYPE, + name, + displayName, + desc, + volumes); + return Result.ATTACHED; + } + } catch (IOException ioe) { + Exceptions.printStackTrace(ioe); + } catch (URISyntaxException use) { + Exceptions.printStackTrace(use); + } + return Result.CANCELED; + } + + private Pair findOwner(final URL root) { + for (LibraryManager lm : LibraryManager.getOpenManagers()) { + for (Library l : lm.getLibraries()) { + if (!J2SELibraryTypeProvider.LIBRARY_TYPE.equals(l.getType())) { + continue; + } + final List cp = l.getContent(J2SELibraryTypeProvider.VOLUME_TYPE_CLASSPATH); + if (cp.contains(root)) { + return Pair.of(lm, l); + } + } + } + return null; + } + + private static class Pair { + public final F first; + public final S second; + + private Pair(F first, S second) { + this.first = first; + this.second = second; + } + + public static Pair of(F first, S second) { + return new Pair(first,second); + } + } + +} diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SEVolumeCustomizer.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SEVolumeCustomizer.java --- a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SEVolumeCustomizer.java +++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SEVolumeCustomizer.java @@ -55,9 +55,11 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.Collection; import java.util.Arrays; import java.util.Collections; +import java.util.List; import javax.swing.AbstractAction; import javax.swing.DefaultListCellRenderer; import javax.swing.JComponent; @@ -67,6 +69,9 @@ import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileFilter; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; import org.netbeans.api.project.ant.FileChooser; import org.netbeans.spi.java.project.support.JavadocAndSourceRootDetection; import org.netbeans.spi.project.libraries.LibraryCustomizerContext; @@ -116,6 +121,72 @@ this.upButton.setEnabled(enabled && indices.length>0 && indices[0]>0); } + @CheckForNull + static URI[] select( + @NonNull final String volumeType, + @NonNull final String libName, + @NonNull final File[] lastFolder, + @NullAllowed final Component owner, + @NullAllowed final File baseFolder) { + assert volumeType != null; + assert lastFolder != null; + assert lastFolder.length == 1; + final File libFolder = baseFolder == null ? + null: + FileUtil.normalizeFile(new File(baseFolder, libName)); + FileChooser chooser = new FileChooser(baseFolder, libFolder); + // for now variable based paths are disabled in library definition + // can be revisit if it is needed + chooser.setFileHidingEnabled(false); + chooser.setAcceptAllFileFilterUsed(false); + if (volumeType.equals(J2SELibraryTypeProvider.VOLUME_TYPE_CLASSPATH)) { + chooser.setMultiSelectionEnabled (true); + chooser.setDialogTitle(NbBundle.getMessage(J2SEVolumeCustomizer.class,"TXT_OpenClasses")); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + chooser.setFileFilter (new ArchiveFileFilter(NbBundle.getMessage( + J2SEVolumeCustomizer.class,"TXT_Classpath"),new String[] {"ZIP","JAR"})); //NOI18N + chooser.setApproveButtonText(NbBundle.getMessage(J2SEVolumeCustomizer.class,"CTL_SelectCP")); + chooser.setApproveButtonMnemonic(NbBundle.getMessage(J2SEVolumeCustomizer.class,"MNE_SelectCP").charAt(0)); + } else if (volumeType.equals(J2SELibraryTypeProvider.VOLUME_TYPE_JAVADOC)) { + chooser.setMultiSelectionEnabled (true); + chooser.setDialogTitle(NbBundle.getMessage(J2SEVolumeCustomizer.class,"TXT_OpenJavadoc")); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + chooser.setFileFilter (new ArchiveFileFilter(NbBundle.getMessage( + J2SEVolumeCustomizer.class,"TXT_Javadoc"),new String[] {"ZIP","JAR"})); //NOI18N + chooser.setApproveButtonText(NbBundle.getMessage(J2SEVolumeCustomizer.class,"CTL_SelectJD")); + chooser.setApproveButtonMnemonic(NbBundle.getMessage(J2SEVolumeCustomizer.class,"MNE_SelectJD").charAt(0)); + } else if (volumeType.equals(J2SELibraryTypeProvider.VOLUME_TYPE_SRC)) { + chooser.setMultiSelectionEnabled (true); + chooser.setDialogTitle(NbBundle.getMessage(J2SEVolumeCustomizer.class,"TXT_OpenSources")); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + chooser.setFileFilter (new ArchiveFileFilter(NbBundle.getMessage( + J2SEVolumeCustomizer.class,"TXT_Sources"),new String[] {"ZIP","JAR"})); //NOI18N + chooser.setApproveButtonText(NbBundle.getMessage(J2SEVolumeCustomizer.class,"CTL_SelectSRC")); + chooser.setApproveButtonMnemonic(NbBundle.getMessage(J2SEVolumeCustomizer.class,"MNE_SelectSRC").charAt(0)); + } + if (lastFolder[0] != null) { + chooser.setCurrentDirectory (lastFolder[0]); + } else if (baseFolder != null) { + chooser.setCurrentDirectory (baseFolder); + } + if (chooser.showOpenDialog(owner) == JFileChooser.APPROVE_OPTION) { + try { + lastFolder[0] = chooser.getCurrentDirectory(); + return pathsToURIs ( + chooser.getSelectedPaths(), + volumeType, + baseFolder); + } catch (MalformedURLException mue) { + Exceptions.printStackTrace(mue); + } catch (URISyntaxException ue) { + Exceptions.printStackTrace(ue); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + return null; + } + private void postInitComponents () { this.content.setCellRenderer(new ContentRenderer()); @@ -346,54 +417,16 @@ }//GEN-LAST:event_removeResource private void addResource(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addResource - File baseFolder = null; - File libFolder = null; - if (allowRelativePaths != null && allowRelativePaths.booleanValue()) { - baseFolder = FileUtil.normalizeFile(new File(URI.create(area.getLocation().toExternalForm())).getParentFile()); - libFolder = FileUtil.normalizeFile(new File(baseFolder, impl.getName())); - } - FileChooser chooser = new FileChooser(baseFolder, libFolder); - FileUtil.preventFileChooserSymlinkTraversal(chooser, null); - // for now variable based paths are disabled in library definition - // can be revisit if it is needed - chooser.setFileHidingEnabled(false); - chooser.setAcceptAllFileFilterUsed(false); - if (this.volumeType.equals(J2SELibraryTypeProvider.VOLUME_TYPE_CLASSPATH)) { - chooser.setMultiSelectionEnabled (true); - chooser.setDialogTitle(NbBundle.getMessage(J2SEVolumeCustomizer.class,"TXT_OpenClasses")); - chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); - chooser.setFileFilter (new ArchiveFileFilter(NbBundle.getMessage( - J2SEVolumeCustomizer.class,"TXT_Classpath"),new String[] {"ZIP","JAR"})); //NOI18N - chooser.setApproveButtonText(NbBundle.getMessage(J2SEVolumeCustomizer.class,"CTL_SelectCP")); - chooser.setApproveButtonMnemonic(NbBundle.getMessage(J2SEVolumeCustomizer.class,"MNE_SelectCP").charAt(0)); - } - else if (this.volumeType.equals(J2SELibraryTypeProvider.VOLUME_TYPE_JAVADOC)) { - chooser.setMultiSelectionEnabled (true); - chooser.setDialogTitle(NbBundle.getMessage(J2SEVolumeCustomizer.class,"TXT_OpenJavadoc")); - chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); - chooser.setFileFilter (new ArchiveFileFilter(NbBundle.getMessage( - J2SEVolumeCustomizer.class,"TXT_Javadoc"),new String[] {"ZIP","JAR"})); //NOI18N - chooser.setApproveButtonText(NbBundle.getMessage(J2SEVolumeCustomizer.class,"CTL_SelectJD")); - chooser.setApproveButtonMnemonic(NbBundle.getMessage(J2SEVolumeCustomizer.class,"MNE_SelectJD").charAt(0)); - } - else if (this.volumeType.equals(J2SELibraryTypeProvider.VOLUME_TYPE_SRC)) { - chooser.setMultiSelectionEnabled (true); - chooser.setDialogTitle(NbBundle.getMessage(J2SEVolumeCustomizer.class,"TXT_OpenSources")); - chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); - chooser.setFileFilter (new ArchiveFileFilter(NbBundle.getMessage( - J2SEVolumeCustomizer.class,"TXT_Sources"),new String[] {"ZIP","JAR"})); //NOI18N - chooser.setApproveButtonText(NbBundle.getMessage(J2SEVolumeCustomizer.class,"CTL_SelectSRC")); - chooser.setApproveButtonMnemonic(NbBundle.getMessage(J2SEVolumeCustomizer.class,"MNE_SelectSRC").charAt(0)); - } - if (lastFolder != null) { - chooser.setCurrentDirectory (lastFolder); - } else if (baseFolder != null) { - chooser.setCurrentDirectory (baseFolder); - } - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + final boolean arp = allowRelativePaths != null && allowRelativePaths.booleanValue(); + final File baseFolder = arp ? + FileUtil.normalizeFile(new File(URI.create(area.getLocation().toExternalForm())).getParentFile()): + null; + final File[] cwd = new File[]{lastFolder}; + final URI[] res = select(volumeType, impl.getName(), cwd, this, baseFolder); + if (res != null) { try { - lastFolder = chooser.getCurrentDirectory(); - addFiles (chooser.getSelectedPaths(), area != null ? area.getLocation() : null, this.volumeType); + lastFolder = cwd[0]; + addFiles (res, arp); } catch (MalformedURLException mue) { Exceptions.printStackTrace(mue); } catch (URISyntaxException ue) { @@ -435,51 +468,13 @@ } }//GEN-LAST:event_addURLButtonActionPerformed - private void addFiles (String[] fileNames, URL libraryLocation, String volume) throws MalformedURLException, URISyntaxException { + private void addFiles (URI[] toAdd, boolean allowRelativePaths) throws MalformedURLException, URISyntaxException { int firstIndex = this.model.getSize(); - for (int i = 0; i < fileNames.length; i++) { - File f = new File(fileNames[i]); - URI uri = LibrariesSupport.convertFilePathToURI(fileNames[i]); - if (allowRelativePaths != null && allowRelativePaths.booleanValue()) { - File realFile = f; - if (!f.isAbsolute()) { - assert area != null; - if (area != null) { - realFile = FileUtil.normalizeFile(new File( - new File(URI.create(area.getLocation().toExternalForm())).getParentFile(), f.getPath())); - } - } - String jarPath = checkFile(realFile, volume); - if (FileUtil.isArchiveFile(realFile.toURI().toURL())) { - uri = LibrariesSupport.getArchiveRoot(uri); - if (jarPath != null) { - assert uri.toString().endsWith("!/") : uri.toString(); //NOI18N - uri = URI.create(uri.toString() + encodePath(jarPath)); - } - } else if (!uri.toString().endsWith("/")){ //NOI18N - try { - uri = new URI(uri.toString()+"/"); //NOI18N - } catch (URISyntaxException ex) { - throw new AssertionError(ex); - } - } + for (URI uri : toAdd) { + if (allowRelativePaths) { model.addResource(uri); } else { - assert f.isAbsolute() : f.getPath(); - f = FileUtil.normalizeFile (f); - String jarPath = checkFile(f, volume); - uri = f.toURI(); - if (FileUtil.isArchiveFile(uri.toURL())) { - uri = LibrariesSupport.getArchiveRoot(uri); - if (jarPath != null) { - assert uri.toString().endsWith("!/") : uri.toString(); //NOI18N - uri = URI.create(uri.toString() + encodePath(jarPath)); - } - } else if (!uri.toString().endsWith("/")){ //NOI18N - uri = URI.create(uri.toString()+"/"); //NOI18N - } model.addResource(uri.toURL()); //Has to be added as URL, model asserts it - } } int lastIndex = this.model.getSize()-1; @@ -501,7 +496,57 @@ return new URI(null, null, path, null).getRawPath(); } - private String checkFile(File f, String volume) { + @NonNull + private static URI[] pathsToURIs( + @NonNull final String[] fileNames, + @NonNull final String volume, + @NullAllowed final File baseFolder) throws MalformedURLException, URISyntaxException { + final List result = new ArrayList(fileNames.length); + for (int i = 0; i < fileNames.length; i++) { + File f = new File(fileNames[i]); + URI uri = LibrariesSupport.convertFilePathToURI(fileNames[i]); + if (baseFolder != null) { + File realFile = f; + if (!f.isAbsolute()) { + realFile = FileUtil.normalizeFile(new File( + baseFolder, f.getPath())); + } + String jarPath = checkFile(realFile, volume); + if (FileUtil.isArchiveFile(realFile.toURI().toURL())) { + uri = LibrariesSupport.getArchiveRoot(uri); + if (jarPath != null) { + assert uri.toString().endsWith("!/") : uri.toString(); //NOI18N + uri = URI.create(uri.toString() + encodePath(jarPath)); + } + } else if (!uri.toString().endsWith("/")){ //NOI18N + try { + uri = new URI(uri.toString()+"/"); //NOI18N + } catch (URISyntaxException ex) { + throw new AssertionError(ex); + } + } + result.add(uri); + } else { + assert f.isAbsolute() : f.getPath(); + f = FileUtil.normalizeFile (f); + String jarPath = checkFile(f, volume); + uri = f.toURI(); + if (FileUtil.isArchiveFile(uri.toURL())) { + uri = LibrariesSupport.getArchiveRoot(uri); + if (jarPath != null) { + assert uri.toString().endsWith("!/") : uri.toString(); //NOI18N + uri = URI.create(uri.toString() + encodePath(jarPath)); + } + } else if (!uri.toString().endsWith("/")){ //NOI18N + uri = URI.create(uri.toString()+"/"); //NOI18N + } + result.add(uri); + } + } + return result.toArray(new URI[result.size()]); + } + + private static String checkFile(File f, String volume) { FileObject fo = FileUtil.toFileObject(f); if (volume.equals(J2SELibraryTypeProvider.VOLUME_TYPE_JAVADOC)) { if (fo != null) { diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformCustomizer.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformCustomizer.java --- a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformCustomizer.java +++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformCustomizer.java @@ -94,9 +94,9 @@ private static final String CUSTOMIZERS_PATH = "org-netbeans-api-java/platform/j2seplatform/customizers/"; //NOI18N - private static final int CLASSPATH = 0; - private static final int SOURCES = 1; - private static final int JAVADOC = 2; + static final int CLASSPATH = 0; + static final int SOURCES = 1; + static final int JAVADOC = 2; private J2SEPlatformImpl platform; @@ -106,6 +106,68 @@ } + static boolean select( + final PathModel model, + final File[] currentDir, + final Component parentComponent) { + final JFileChooser chooser = new JFileChooser (); + chooser.setMultiSelectionEnabled (true); + String title = null; + String message = null; + String approveButtonName = null; + String approveButtonNameMne = null; + if (model.type == SOURCES) { + title = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_OpenSources"); + message = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_Sources"); + approveButtonName = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_OpenSources"); + approveButtonNameMne = NbBundle.getMessage (J2SEPlatformCustomizer.class,"MNE_OpenSources"); + } else if (model.type == JAVADOC) { + title = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_OpenJavadoc"); + message = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_Javadoc"); + approveButtonName = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_OpenJavadoc"); + approveButtonNameMne = NbBundle.getMessage (J2SEPlatformCustomizer.class,"MNE_OpenJavadoc"); + } + chooser.setDialogTitle(title); + chooser.setApproveButtonText(approveButtonName); + chooser.setApproveButtonMnemonic (approveButtonNameMne.charAt(0)); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + if (Utilities.isMac()) { + //New JDKs and JREs are bundled into package, allow JFileChooser to navigate in + chooser.putClientProperty("JFileChooser.packageIsTraversable", "always"); //NOI18N + } + //#61789 on old macosx (jdk 1.4.1) these two method need to be called in this order. + chooser.setAcceptAllFileFilterUsed( false ); + chooser.setFileFilter (new ArchiveFileFilter(message,new String[] {"ZIP","JAR"})); //NOI18N + if (currentDir[0] != null) { + chooser.setCurrentDirectory(currentDir[0]); + } + if (chooser.showOpenDialog(parentComponent) == JFileChooser.APPROVE_OPTION) { + File[] fs = chooser.getSelectedFiles(); + boolean addingFailed = false; + for (File f : fs) { + //XXX: JFileChooser workaround (JDK bug #5075580), double click on folder returns wrong file + // E.g. for /foo/src it returns /foo/src/src + // Try to convert it back by removing last invalid name component + if (!f.exists()) { + File parent = f.getParentFile(); + if (parent != null && f.getName().equals(parent.getName()) && parent.exists()) { + f = parent; + } + } + if (f.exists()) { + addingFailed|=!model.addPath (f); + } + } + if (addingFailed) { + new NotifyDescriptor.Message (NbBundle.getMessage(J2SEPlatformCustomizer.class,"TXT_CanNotAddResolve"), + NotifyDescriptor.ERROR_MESSAGE); + } + currentDir[0] = FileUtil.normalizeFile(chooser.getCurrentDirectory()); + return true; + } + return false; + } + private void initComponents () { this.getAccessibleContext().setAccessibleName (NbBundle.getMessage(J2SEPlatformCustomizer.class,"AN_J2SEPlatformCustomizer")); this.getAccessibleContext().setAccessibleDescription (NbBundle.getMessage(J2SEPlatformCustomizer.class,"AD_J2SEPlatformCustomizer")); @@ -369,63 +431,10 @@ } private void addPathElement () { - JFileChooser chooser = new JFileChooser (); - FileUtil.preventFileChooserSymlinkTraversal(chooser, null); - chooser.setMultiSelectionEnabled (true); - String title = null; - String message = null; - String approveButtonName = null; - String approveButtonNameMne = null; - if (this.type == SOURCES) { - title = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_OpenSources"); - message = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_Sources"); - approveButtonName = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_OpenSources"); - approveButtonNameMne = NbBundle.getMessage (J2SEPlatformCustomizer.class,"MNE_OpenSources"); - } - else if (this.type == JAVADOC) { - title = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_OpenJavadoc"); - message = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_Javadoc"); - approveButtonName = NbBundle.getMessage (J2SEPlatformCustomizer.class,"TXT_OpenJavadoc"); - approveButtonNameMne = NbBundle.getMessage (J2SEPlatformCustomizer.class,"MNE_OpenJavadoc"); - } - chooser.setDialogTitle(title); - chooser.setApproveButtonText(approveButtonName); - chooser.setApproveButtonMnemonic (approveButtonNameMne.charAt(0)); - chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); - if (Utilities.isMac()) { - //New JDKs and JREs are bundled into package, allow JFileChooser to navigate in - chooser.putClientProperty("JFileChooser.packageIsTraversable", "always"); //NOI18N - } - //#61789 on old macosx (jdk 1.4.1) these two method need to be called in this order. - chooser.setAcceptAllFileFilterUsed( false ); - chooser.setFileFilter (new ArchiveFileFilter(message,new String[] {"ZIP","JAR"})); //NOI18N - if (this.currentDir != null) { - chooser.setCurrentDirectory(this.currentDir); - } - if (chooser.showOpenDialog(this)==JFileChooser.APPROVE_OPTION) { - File[] fs = chooser.getSelectedFiles(); - PathModel model = (PathModel) this.resources.getModel(); - boolean addingFailed = false; - int firstIndex = this.resources.getModel().getSize(); - for (File f : fs) { - //XXX: JFileChooser workaround (JDK bug #5075580), double click on folder returns wrong file - // E.g. for /foo/src it returns /foo/src/src - // Try to convert it back by removing last invalid name component - if (!f.exists()) { - File parent = f.getParentFile(); - if (parent != null && f.getName().equals(parent.getName()) && parent.exists()) { - f = parent; - } - } - if (f.exists()) { - addingFailed|=!model.addPath (f); - } - } - if (addingFailed) { - new NotifyDescriptor.Message (NbBundle.getMessage(J2SEPlatformCustomizer.class,"TXT_CanNotAddResolve"), - NotifyDescriptor.ERROR_MESSAGE); - } - int lastIndex = this.resources.getModel().getSize()-1; + final int firstIndex = this.resources.getModel().getSize(); + final File[] cwd = new File[]{currentDir}; + if (select((PathModel)this.resources.getModel(),cwd, this)) { + final int lastIndex = this.resources.getModel().getSize()-1; if (firstIndex<=lastIndex) { int[] toSelect = new int[lastIndex-firstIndex+1]; for (int i = 0; i < toSelect.length; i++) { @@ -433,7 +442,7 @@ } this.resources.setSelectedIndices(toSelect); } - this.currentDir = FileUtil.normalizeFile(chooser.getCurrentDirectory()); + this.currentDir = cwd[0]; } } @@ -491,7 +500,7 @@ } - private static class PathModel extends AbstractListModel/**/ { + static class PathModel extends AbstractListModel/**/ { private J2SEPlatformImpl platform; private int type; diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformImpl.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformImpl.java --- a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformImpl.java +++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformImpl.java @@ -476,6 +476,19 @@ } return Collections.emptyList(); } + + boolean isBroken () { + if (getInstallFolders().isEmpty()) { + return true; + } + for (String tool : PlatformConvertor.IMPORTANT_TOOLS) { + if (findTool(tool) == null) { + return true; + } + } + return false; + } + private static final Map OFFICIAL_JAVADOC = new HashMap(); static { OFFICIAL_JAVADOC.put("1.0", null); // NOI18N diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformNode.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformNode.java --- a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformNode.java +++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformNode.java @@ -67,7 +67,7 @@ } public String getHtmlDisplayName() { - if (isBroken()) { + if (platform.isBroken()) { return ""+this.platform.getDisplayName()+""; } else { @@ -103,7 +103,7 @@ } public java.awt.Component getCustomizer () { - if (isBroken()) { + if (platform.isBroken()) { return new BrokenPlatformCustomizer (this.platform); } else { @@ -111,16 +111,4 @@ } } - private boolean isBroken () { - if (this.platform.getInstallFolders().size()==0) { - return true; - } - for (String tool : PlatformConvertor.IMPORTANT_TOOLS) { - if (platform.findTool(tool) == null) { - return true; - } - } - return false; - } - } diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformSourceJavadocAttacher.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformSourceJavadocAttacher.java new file mode 100644 --- /dev/null +++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/platformdefinition/J2SEPlatformSourceJavadocAttacher.java @@ -0,0 +1,102 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.j2seplatform.platformdefinition; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.platform.JavaPlatform; +import org.netbeans.api.java.platform.JavaPlatformManager; +import org.netbeans.api.java.platform.Specification; +import org.netbeans.spi.java.queries.SourceJavadocAttacherImplementation; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author Tomas Zezula + */ +@ServiceProvider(service=SourceJavadocAttacherImplementation.class,position=150) +public class J2SEPlatformSourceJavadocAttacher implements SourceJavadocAttacherImplementation { + + @Override + public Result attachSources(final URL root) throws IOException { + return attach(root, J2SEPlatformCustomizer.SOURCES); + } + + @Override + public Result attachJavadoc(final URL root) throws IOException { + return attach(root, J2SEPlatformCustomizer.JAVADOC); + } + + private Result attach(final URL root, int mode) { + final J2SEPlatformImpl platform = findOwner(root); + if (platform == null) { + return Result.UNSUPPORTED; + } + final J2SEPlatformCustomizer.PathModel model = new J2SEPlatformCustomizer.PathModel(platform, mode); + if (J2SEPlatformCustomizer.select(model,new File[1],null)) { + return Result.ATTACHED; + } + return Result.CANCELED; + } + + private J2SEPlatformImpl findOwner(final URL root) { + for (JavaPlatform p : JavaPlatformManager.getDefault().getPlatforms(null, new Specification(J2SEPlatformImpl.PLATFORM_J2SE, null))) { + if (!(p instanceof J2SEPlatformImpl)) { + //Cannot handle unknown platform + continue; + } + final J2SEPlatformImpl j2sep = (J2SEPlatformImpl) p; + if (j2sep.isBroken()) { + continue; + } + for (ClassPath.Entry entry : j2sep.getBootstrapLibraries().entries()) { + if (root.equals(entry.getURL())) { + return j2sep; + } + } + } + return null; + } + +} diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultJavadocForBinaryQuery.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultJavadocForBinaryQuery.java new file mode 100644 --- /dev/null +++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultJavadocForBinaryQuery.java @@ -0,0 +1,64 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.j2seplatform.queries; + +import java.net.URL; +import java.util.Map; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.java.queries.JavadocForBinaryQuery; +import org.netbeans.api.java.queries.JavadocForBinaryQuery.Result; +import org.netbeans.spi.java.queries.JavadocForBinaryQueryImplementation; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author Tomas Zezula + */ +@ServiceProvider(service=JavadocForBinaryQueryImplementation.class) //position = last +public class DefaultJavadocForBinaryQuery implements JavadocForBinaryQueryImplementation { + + @Override + public Result findJavadoc(@NonNull final URL binaryRoot) { + final Map mapping = QueriesCache.getJavadoc().getRoots(); + return mapping.get(binaryRoot); + } +} diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultSourceForBinaryQuery.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultSourceForBinaryQuery.java new file mode 100644 --- /dev/null +++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultSourceForBinaryQuery.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.j2seplatform.queries; + +import java.net.URL; +import java.util.Map; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.java.queries.SourceForBinaryQuery; +import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation2; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author Tomas Zezula + */ +@ServiceProvider(service=SourceForBinaryQueryImplementation2.class) //position = last +public class DefaultSourceForBinaryQuery implements SourceForBinaryQueryImplementation2 { + + @Override + public Result findSourceRoots2(@NonNull final URL binaryRoot) { + final Map mapping = QueriesCache.getSources().getRoots(); + return mapping.get(binaryRoot); + } + + @Override + public SourceForBinaryQuery.Result findSourceRoots(URL binaryRoot) { + return findSourceRoots2(binaryRoot); + } + +} diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultSourceJavadocAttacher.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultSourceJavadocAttacher.java new file mode 100644 --- /dev/null +++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/DefaultSourceJavadocAttacher.java @@ -0,0 +1,140 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.j2seplatform.queries; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileFilter; +import org.netbeans.spi.java.project.support.JavadocAndSourceRootDetection; +import org.netbeans.spi.java.queries.SourceJavadocAttacherImplementation; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileStateInvalidException; +import org.openide.filesystems.FileUtil; +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author Tomas Zezula + */ +@ServiceProvider(service=SourceJavadocAttacherImplementation.class) //position=last +public class DefaultSourceJavadocAttacher implements SourceJavadocAttacherImplementation { + + @Override + public Result attachSources(URL root) throws IOException { + final URL[] sources = selectRoots(0); + if (sources != null) { + QueriesCache.getSources().updateRoot(root, sources); + return Result.ATTACHED; + } + return Result.CANCELED; + } + + @Override + public Result attachJavadoc(URL root) throws IOException { + final URL[] javadoc = selectRoots(1); + if (javadoc != null) { + QueriesCache.getSources().updateRoot(root, javadoc); + return Result.ATTACHED; + } + return Result.CANCELED; + } + + @NbBundle.Messages({ + "TXT_Title=Browse ZIP/Folder", + "TXT_Javadoc=Library Javadoc (folder, ZIP or JAR file)", + "TXT_Sources=Library Sources (folder, ZIP or JAR file)", + "TXT_Select=Add ZIP/Folder", + "MNE_Select=A" + }) + private static URL[] selectRoots(final int mode) throws MalformedURLException, FileStateInvalidException { + final JFileChooser chooser = new JFileChooser(); + chooser.setMultiSelectionEnabled (true); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + chooser.setDialogTitle(Bundle.TXT_Title()); + chooser.setFileFilter (new FileFilter() { + @Override + public boolean accept(File f) { + try { + return f.isDirectory() || + FileUtil.isArchiveFile(f.toURI().toURL()); + } catch (MalformedURLException ex) { + return false; + } + } + + @Override + public String getDescription() { + return mode == 0 ? Bundle.TXT_Sources() : Bundle.TXT_Javadoc(); + } + }); + chooser.setApproveButtonText(Bundle.TXT_Select()); + chooser.setApproveButtonMnemonic(Bundle.MNE_Select().charAt(0)); + if (currentFolder != null) { + chooser.setCurrentDirectory(currentFolder); + } + if (chooser.showOpenDialog(null) == chooser.APPROVE_OPTION) { + currentFolder = chooser.getCurrentDirectory(); + final File[] files = chooser.getSelectedFiles(); + final List result = new ArrayList(files.length); + for (File f : files) { + FileObject fo = FileUtil.toFileObject(f); + if (fo.isData()) { + fo = FileUtil.getArchiveRoot(fo); + } + fo = mode == 0 ? + JavadocAndSourceRootDetection.findSourceRoot(fo) : + JavadocAndSourceRootDetection.findJavadocRoot(fo); + result.add(fo.getURL()); + } + return result.toArray(new URL[result.size()]); + } + return null; + } + + private static File currentFolder; +} diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/QueriesCache.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/QueriesCache.java new file mode 100644 --- /dev/null +++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/queries/QueriesCache.java @@ -0,0 +1,268 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.j2seplatform.queries; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; +import javax.swing.event.ChangeListener; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.java.queries.JavadocForBinaryQuery; +import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation2; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.URLMapper; +import org.openide.util.ChangeSupport; +import org.openide.util.Exceptions; +import org.openide.util.NbPreferences; + +/** + * + * @author Tomas Zezula + */ +final class QueriesCache { + + private static QueriesCache javadoc; + private static QueriesCache sources; + private static final char SEP = '-'; //NOI18N + + private final Object lck = new Object(); + private final Class clazz; + //@GuardedBy("lck") + private Map cache; + + private QueriesCache(@NonNull final Class clazz){ + assert clazz != null; + this.clazz = clazz; + } + + @NonNull + Map getRoots() { + return Collections.unmodifiableMap(loadRoots()); + } + + void updateRoot(final URL binaryRoot, final URL... rootsToAttach) { + T currentMapping = null; + synchronized (lck) { + final Map currentRoots = loadRoots(); + currentMapping = currentRoots.get(binaryRoot); + final Preferences root = NbPreferences.forModule(QueriesCache.class); + final Preferences node = root.node(clazz.getSimpleName()); + final String binaryRootStr = binaryRoot.toExternalForm(); + try { + for (String key : filterKeys(node.keys(),binaryRootStr)) { + node.remove(key); + } + for (int i=0; i < rootsToAttach.length; i++) { + node.put(String.format("%s-%d",binaryRootStr,i), rootsToAttach[i].toExternalForm()); + } + node.flush(); + } catch (BackingStoreException bse) { + Exceptions.printStackTrace(bse); + } + if (currentMapping == null) { + try { + currentMapping = clazz.newInstance(); + currentRoots.put(binaryRoot, currentMapping); + } catch (InstantiationException ie) { + Exceptions.printStackTrace(ie); + } catch (IllegalAccessException iae) { + Exceptions.printStackTrace(iae); + } + } + } + if (currentMapping != null) { + currentMapping.update(Arrays.asList(rootsToAttach)); + } + } + + @NonNull + private Map loadRoots() { + synchronized(lck) { + if (cache == null) { + Map result = new HashMap(); + final Preferences root = NbPreferences.forModule(QueriesCache.class); + final String folder = clazz.getSimpleName(); + try { + if (root.nodeExists(folder)) { + final Preferences node = root.node(folder); + Map> bindings = new HashMap>(); + for (String key : node.keys()) { + final String value = node.get(key, null); + if (value != null) { + final URL binUrl = getURL(key); + List binding = bindings.get(binUrl); + if(binding == null) { + binding = new ArrayList(); + bindings.put(binUrl,binding); + } + binding.add(new URL(value)); + } + } + for (Map.Entry> e : bindings.entrySet()) { + final T instance = clazz.newInstance(); + instance.update(e.getValue()); + result.put(e.getKey(), instance); + } + } + } catch (BackingStoreException bse) { + Exceptions.printStackTrace(bse); + } catch (MalformedURLException mue) { + Exceptions.printStackTrace(mue); + } catch (InstantiationException ie) { + Exceptions.printStackTrace(ie); + } catch (IllegalAccessException iae) { + Exceptions.printStackTrace(iae); + } + cache = result; + } + return cache; + } + } + + private URL getURL(@NonNull final String pattern) throws MalformedURLException { + final int index = pattern.lastIndexOf(SEP); //NOI18N + assert index > 0; + return new URL(pattern.substring(0, index)); + } + + private Iterable filterKeys( + final String[] keys, + final String binaryRoot) { + final List result = new ArrayList(); + for (int i=0; i getJavadoc() { + if (javadoc == null) { + javadoc = new QueriesCache(Javadoc.class); + } + return javadoc; + } + + @NonNull + static synchronized QueriesCache getSources() { + if (sources == null) { + sources = new QueriesCache(Sources.class); + } + return sources; + } + + static abstract class ResultBase { + private final ChangeSupport cs = new ChangeSupport(this); + + public void addChangeListener(@NonNull final ChangeListener l) { + cs.addChangeListener(l); + } + + public void removeChangeListener(@NonNull final ChangeListener l) { + cs.removeChangeListener(l); + } + + public final void update(final Collection roots) { + updateImpl(roots); + cs.fireChange(); + } + + protected abstract void updateImpl(final Collection roots); + } + + static class Javadoc extends ResultBase implements JavadocForBinaryQuery.Result { + private volatile URL[] roots; + + public URL[] getRoots() { + final URL[] tmp = roots; + return tmp == null ? new URL[0] : Arrays.copyOf(tmp, tmp.length); + } + + @Override + protected void updateImpl(final Collection roots) { + this.roots = roots.toArray(new URL[roots.size()]); + } + } + + static class Sources extends ResultBase implements SourceForBinaryQueryImplementation2.Result { + private volatile FileObject[] roots; + + @Override + public boolean preferSources() { + return false; + } + + @Override + public FileObject[] getRoots() { + final FileObject[] tmp = roots; + return tmp == null ? new FileObject[0]: Arrays.copyOf(tmp, tmp.length); + } + + @Override + protected void updateImpl(final Collection roots) { + //Todo: replace by classpath to handle fo listening + final List fos = new ArrayList(roots.size()); + for (URL url : roots) { + final FileObject fo = URLMapper.findFileObject(url); + if (fo != null) { + fos.add(fo); + } + } + this.roots = fos.toArray(new FileObject[fos.size()]); + } + } +} diff --git a/java.source/nbproject/project.xml b/java.source/nbproject/project.xml --- a/java.source/nbproject/project.xml +++ b/java.source/nbproject/project.xml @@ -64,7 +64,7 @@ 1 - 1.28 + 1.35 diff --git a/java.source/src/org/netbeans/modules/java/classfile/AttachSourcePanel.form b/java.source/src/org/netbeans/modules/java/classfile/AttachSourcePanel.form new file mode 100644 --- /dev/null +++ b/java.source/src/org/netbeans/modules/java/classfile/AttachSourcePanel.form @@ -0,0 +1,66 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/java.source/src/org/netbeans/modules/java/classfile/AttachSourcePanel.java b/java.source/src/org/netbeans/modules/java/classfile/AttachSourcePanel.java new file mode 100644 --- /dev/null +++ b/java.source/src/org/netbeans/modules/java/classfile/AttachSourcePanel.java @@ -0,0 +1,167 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development and + * Distribution License("CDDL") (collectively, 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-gplv2.html or + * nbbuild/licenses/CDDL-GPL-2-CP. See the License for the specific language + * governing permissions and limitations under the License. When distributing + * the software, include this License Header Notice in each file and include + * the License file at nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided by + * Oracle in the GPL Version 2 section of the License file that accompanied + * this code. If applicable, add the following below the License Header, with + * the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you do not indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to its + * licensees as provided above. However, if you add GPL Version 2 code and + * therefore, elected the GPL Version 2 license, then the option applies only + * if the new code is made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ + +/* + * AttachSourcePanel.java + * + * Created on Aug 1, 2011, 4:54:38 PM + */ +package org.netbeans.modules.java.classfile; + +import java.net.URL; +import org.netbeans.api.actions.Openable; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.queries.SourceForBinaryQuery; +import org.netbeans.api.java.queries.SourceJavadocAttacher; +import org.netbeans.spi.java.classpath.support.ClassPathSupport; +import org.openide.cookies.EditorCookie; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.filesystems.URLMapper; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObjectNotFoundException; +import org.openide.util.Exceptions; +import org.openide.util.NbBundle; + + +public class AttachSourcePanel extends javax.swing.JPanel { + + private final URL root; + private final URL file; + private final String binaryName; + + public AttachSourcePanel( + @NonNull final URL root, + @NonNull final URL file, + @NonNull final String binaryName) { + assert root != null; + assert file != null; + assert binaryName != null; + this.root = root; + this.file = file; + this.binaryName = binaryName; + initComponents(); + } + + @NbBundle.Messages({ + "TXT_UnknownRoot=" + }) + private String getFileDisplayName() { + final FileObject rootFo = URLMapper.findFileObject(root); + return rootFo == null ? Bundle.TXT_UnknownRoot() : FileUtil.getFileDisplayName(rootFo); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jLabel1 = new javax.swing.JLabel(); + jButton1 = new javax.swing.JButton(); + jLabel2 = new javax.swing.JLabel(); + + jLabel1.setText(org.openide.util.NbBundle.getMessage(AttachSourcePanel.class, "AttachSourcePanel.jLabel1.text")); // NOI18N + + jButton1.setText(org.openide.util.NbBundle.getMessage(AttachSourcePanel.class, "AttachSourcePanel.jButton1.text")); // NOI18N + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + attachSources(evt); + } + }); + + jLabel2.setText(getFileDisplayName()); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(20, 20, 20) + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, 430, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton1) + .addGap(17, 17, 17)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1) + .addComponent(jLabel2) + .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); + }// //GEN-END:initComponents + +private void attachSources(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_attachSources + if (SourceJavadocAttacher.attachSources(root)) { + final FileObject rootFo = URLMapper.findFileObject(root); + final FileObject fileFo = URLMapper.findFileObject(file); + if (rootFo != null && fileFo != null) { + final FileObject[] fos = SourceForBinaryQuery.findSourceRoots(root).getRoots(); + if (fos.length > 0) { + final ClassPath cp = ClassPathSupport.createClassPath(fos); + final FileObject newFileFo = cp.findResource(binaryName + ".java"); //NOI18N + if (newFileFo != null) { + try { + final EditorCookie ec = DataObject.find(fileFo).getLookup().lookup(EditorCookie.class); + final Openable open = DataObject.find(newFileFo).getLookup().lookup(Openable.class); + if (ec != null && open != null) { + ec.close(); + open.open(); + } + } catch (DataObjectNotFoundException ex) { + Exceptions.printStackTrace(ex); + } + } + } + } + } +}//GEN-LAST:event_attachSources + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jButton1; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + // End of variables declaration//GEN-END:variables +} diff --git a/java.source/src/org/netbeans/modules/java/classfile/Bundle.properties b/java.source/src/org/netbeans/modules/java/classfile/Bundle.properties new file mode 100644 --- /dev/null +++ b/java.source/src/org/netbeans/modules/java/classfile/Bundle.properties @@ -0,0 +1,2 @@ +AttachSourcePanel.jLabel1.text=No source associated with: +AttachSourcePanel.jButton1.text=Associate Source... diff --git a/java.source/src/org/netbeans/modules/java/classfile/CodeGenerator.java b/java.source/src/org/netbeans/modules/java/classfile/CodeGenerator.java --- a/java.source/src/org/netbeans/modules/java/classfile/CodeGenerator.java +++ b/java.source/src/org/netbeans/modules/java/classfile/CodeGenerator.java @@ -44,6 +44,7 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.net.URL; import java.security.MessageDigest; import java.util.Arrays; import java.util.Collections; @@ -96,8 +97,11 @@ private static final Logger LOG = Logger.getLogger(CodeGenerator.class.getName()); private static final Set UNUSABLE_KINDS = EnumSet.of(ElementKind.PACKAGE); - private static final String HASH_ATTRIBUTE_NAME = "origin-hash"; - private static final String DISABLE_ERRORS = "disable-java-errors"; + private static final byte VERSION = 2; + private static final String HASH_ATTRIBUTE_NAME = "origin-hash"; //NOI18N + private static final String DISABLE_ERRORS = "disable-java-errors"; //NOI18N + static final String CLASSFILE_ROOT = "classfile-root"; //NOI18N + static final String CLASSFILE_BINNAME = "classfile-binaryName"; //NOI18N public static FileObject generateCode(final ClasspathInfo cpInfo, final ElementHandle toOpenHandle) { if (UNUSABLE_KINDS.contains(toOpenHandle.getKind())) { @@ -117,6 +121,8 @@ JavaSource js = JavaSource.create(cpInfo, file); final FileObject[] result = new FileObject[1]; final boolean[] sourceGenerated = new boolean[1]; + final URL[] classfileRoot = new URL[1]; + final String[] binaryName = new String[1]; ModificationResult r = js.runModificationTask(new Task() { @Override @@ -135,7 +141,8 @@ return; } - final String resourceName = te.getQualifiedName().toString().replace('.', '/') + ".class"; //NOI18N + final String binName = te.getQualifiedName().toString().replace('.', '/'); //NOI18N + final String resourceName = binName + ".class"; //NOI18N final FileObject resource = cp.findResource(resourceName); if (resource == null) { LOG.info("Cannot find resource: " + resourceName +" on classpath: " + cp.toString()); //NOI18N @@ -148,6 +155,8 @@ return ; } + classfileRoot[0] = root.getURL(); + binaryName[0] = binName; final File sourceRoot = new File (JavaIndex.getIndex(root.getURL()),"gensrc"); //NOI18N final FileObject sourceRootFO = FileUtil.createFolder(sourceRoot); if (sourceRootFO == null) { @@ -155,10 +164,11 @@ return ; } - final String path = te.getQualifiedName().toString().replace('.', '/') + ".java"; //NOI18N + final String path = binName + ".java"; //NOI18N final FileObject source = sourceRootFO.getFileObject(path); MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(VERSION); byte[] hashBytes = md.digest(resource.asBytes()); StringBuilder hashBuilder = new StringBuilder(); @@ -207,6 +217,8 @@ if (resultFile != null) { resultFile.setReadOnly(); result[0].setAttribute(DISABLE_ERRORS, true); + result[0].setAttribute(CLASSFILE_ROOT, classfileRoot[0]); + result[0].setAttribute(CLASSFILE_BINNAME, binaryName[0]); } } diff --git a/java.source/src/org/netbeans/modules/java/classfile/SideBarFactoryImpl.java b/java.source/src/org/netbeans/modules/java/classfile/SideBarFactoryImpl.java new file mode 100644 --- /dev/null +++ b/java.source/src/org/netbeans/modules/java/classfile/SideBarFactoryImpl.java @@ -0,0 +1,92 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development and + * Distribution License("CDDL") (collectively, 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-gplv2.html or + * nbbuild/licenses/CDDL-GPL-2-CP. See the License for the specific language + * governing permissions and limitations under the License. When distributing + * the software, include this License Header Notice in each file and include + * the License file at nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided by + * Oracle in the GPL Version 2 section of the License file that accompanied + * this code. If applicable, add the following below the License Header, with + * the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you do not indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to its + * licensees as provided above. However, if you add GPL Version 2 code and + * therefore, elected the GPL Version 2 license, then the option applies only + * if the new code is made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.classfile; + +import java.net.URL; +import javax.swing.JComponent; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.editor.SideBarFactory; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileStateInvalidException; +import org.openide.loaders.DataObject; +import org.openide.util.Exceptions; + +/** + * + * @author lahvac + */ +public class SideBarFactoryImpl implements SideBarFactory { + + @Override + public JComponent createSideBar(JTextComponent target) { + Document doc = target.getDocument(); + FileObject originFile; + Object origin = doc.getProperty(Document.StreamDescriptionProperty); + + if (origin instanceof DataObject) { + originFile = ((DataObject) origin).getPrimaryFile(); + } else if (origin instanceof FileObject) { + originFile = (FileObject) origin; + } else { + originFile = null; + } + + Object classFileRoot; + Object binaryName; + if (originFile != null) { + classFileRoot = originFile.getAttribute(CodeGenerator.CLASSFILE_ROOT); + binaryName = originFile.getAttribute(CodeGenerator.CLASSFILE_BINNAME); + } else { + classFileRoot = binaryName = null; + } + + if (classFileRoot instanceof URL && binaryName instanceof String) { + try { + return new AttachSourcePanel( + (URL) classFileRoot, + originFile.getURL(), + (String) binaryName); + } catch (FileStateInvalidException ex) { + Exceptions.printStackTrace(ex); + } + } + return null; + } + +} diff --git a/java.source/src/org/netbeans/modules/java/source/resources/layer.xml b/java.source/src/org/netbeans/modules/java/source/resources/layer.xml --- a/java.source/src/org/netbeans/modules/java/source/resources/layer.xml +++ b/java.source/src/org/netbeans/modules/java/source/resources/layer.xml @@ -285,6 +285,13 @@ + + + + + + + diff --git a/java.sourceui/nbproject/project.xml b/java.sourceui/nbproject/project.xml --- a/java.sourceui/nbproject/project.xml +++ b/java.sourceui/nbproject/project.xml @@ -11,7 +11,7 @@ 1 - 1.18 + 1.35
diff --git a/java.sourceui/src/org/netbeans/api/java/source/ui/Bundle.properties b/java.sourceui/src/org/netbeans/api/java/source/ui/Bundle.properties --- a/java.sourceui/src/org/netbeans/api/java/source/ui/Bundle.properties +++ b/java.sourceui/src/org/netbeans/api/java/source/ui/Bundle.properties @@ -54,6 +54,8 @@ array_length_field_javadoc=Length of array. class_constant_javadoc=java.lang.Class constant. javadoc_content_not_found=Javadoc not found. Either Javadoc documentation for this item does not exist or you have not added specified Javadoc in the Java Platform Manager or the Library Manager. +javadoc_content_not_found_attach=Javadoc not found. Either Javadoc documentation for this item does not exist or you have not added specified Javadoc in the Java Platform Manager or the Library Manager.

\ + Associate Javadoc to {1} LBL_HTTPJavadocDownload=Downloading HTTP Javadoc LBL_CancelAction=Cancel {0} diff --git a/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java b/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java --- a/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java +++ b/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java @@ -59,6 +59,9 @@ import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; +import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.queries.JavadocForBinaryQuery; +import org.netbeans.api.java.queries.SourceJavadocAttacher; import org.netbeans.api.java.source.ClasspathInfo; import org.netbeans.api.java.source.CompilationController; import org.netbeans.api.java.source.CompilationInfo; @@ -73,8 +76,10 @@ import org.netbeans.modules.java.preprocessorbridge.api.JavaSourceUtil; import org.netbeans.modules.java.source.JavadocHelper; import org.netbeans.modules.java.source.usages.Pair; +import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileStateInvalidException; +import org.openide.filesystems.FileUtil; import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; @@ -85,7 +90,8 @@ * @author Dusan Balek, Petr Hrebejk */ public class ElementJavadoc { - + + private static final String ASSOCIATE_JDOC = "associate-javadoc:"; //NOI18N private static final String API = "/api"; //NOI18N private static final Set LANGS; @@ -100,10 +106,9 @@ LANGS = Collections.unmodifiableSet(locNames); } - private ElementJavadoc() { - } - private ClasspathInfo cpInfo; + private final ClasspathInfo cpInfo; + private final ElementHandle handle; //private Doc doc; private volatile Future content; private Hashtable> links = new Hashtable>(); @@ -184,6 +189,15 @@ * @return ElementJavadoc describing the javadoc of liked element */ public ElementJavadoc resolveLink(final String link) { + if (link.startsWith(ASSOCIATE_JDOC)) { + final String root = link.substring(ASSOCIATE_JDOC.length()); + try { + SourceJavadocAttacher.attachJavadoc(new URL(root)); + } catch (MalformedURLException ex) { + Exceptions.printStackTrace(ex); + } + return null; + } final ElementJavadoc[] ret = new ElementJavadoc[1]; try { final ElementHandle linkDoc = links.get(link); @@ -273,6 +287,7 @@ private ElementJavadoc(CompilationInfo compilationInfo, Element element, URL url, final Callable cancel) { Pair context = Pair.of(compilationInfo.getTrees(), compilationInfo.getElementUtilities()); this.cpInfo = compilationInfo.getClasspathInfo(); + this.handle = element == null ? null : ElementHandle.create(element); Doc doc = context.second.javaDocFor(element); boolean localized = false; StringBuilder content = new StringBuilder(); @@ -287,7 +302,6 @@ if (!localized) { final FileObject fo = SourceUtils.getFile(element, compilationInfo.getClasspathInfo()); if (fo != null) { - final ElementHandle handle = ElementHandle.create(element); goToSource = new AbstractAction() { public void actionPerformed(ActionEvent evt) { ElementOpen.open(fo, handle); @@ -311,7 +325,6 @@ this.content = prepareContent(content, doc,localized, page, cancel, true, context); } catch (RemoteJavadocException re) { final FileObject fo = compilationInfo.getFileObject(); - final ElementHandle handle = ElementHandle.create(element); final StringBuilder contentFin = content; final boolean localizedFin = localized; this.content = new FutureTask(new Callable(){ @@ -346,6 +359,8 @@ assert url != null; this.content = null; this.docURL = url; + this.handle = null; + this.cpInfo = null; } // Private section --------------------------------------------------------- @@ -635,7 +650,7 @@ if (jdText != null) sb.append(jdText); else - sb.append(NbBundle.getMessage(ElementJavadoc.class, "javadoc_content_not_found")); //NOI18N + sb.append(noJavadocFound()); //NOI18N sb.append("

"); //NOI18N return sb.toString(); } @@ -648,14 +663,52 @@ } return task; } - sb.append(NbBundle.getMessage(ElementJavadoc.class, "javadoc_content_not_found")); //NOI18N + sb.append(noJavadocFound()); //NOI18N return new Now (sb.toString()); } finally { if (page != null) page.close(); } } - + + private String noJavadocFound() { + final List cps = new ArrayList(2); + ClassPath cp = cpInfo.getClassPath(ClasspathInfo.PathKind.BOOT); + if (cp != null) { + cps.add(cp); + } + cp = cpInfo.getClassPath(ClasspathInfo.PathKind.COMPILE); + if (cp != null) { + cps.add(cp); + } + cp = ClassPathSupport.createProxyClassPath(cps.toArray(new ClassPath[cps.size()])); + String toSearch = SourceUtils.getJVMSignature(handle)[0].replace('.', '/'); + if (handle.getKind() != ElementKind.PACKAGE) { + toSearch = toSearch + ".class"; //NOI18N + } + final FileObject resource = cp.findResource(toSearch); + if (resource != null) { + final FileObject root = cp.findOwnerRoot(resource); + try { + final URL rootURL = root.getURL(); + if (JavadocForBinaryQuery.findJavadoc(rootURL).getRoots().length == 0) { + FileObject userRoot = FileUtil.getArchiveFile(root); + if (userRoot == null) { + userRoot = root; + } + return NbBundle.getMessage( + ElementJavadoc.class, + "javadoc_content_not_found_attach", + rootURL.toExternalForm(), + FileUtil.getFileDisplayName(userRoot)); + } + } catch (FileStateInvalidException ex) { + Exceptions.printStackTrace(ex); + } + } + return NbBundle.getMessage(ElementJavadoc.class, "javadoc_content_not_found"); + } + private CharSequence getContainingClassOrPackageHeader(ProgramElementDoc peDoc, Pair ctx) { StringBuilder sb = new StringBuilder(); ClassDoc cls = peDoc.containingClass(); diff --git a/maven.embedder/src/org/netbeans/modules/maven/embedder/EmbedderFactory.java b/maven.embedder/src/org/netbeans/modules/maven/embedder/EmbedderFactory.java --- a/maven.embedder/src/org/netbeans/modules/maven/embedder/EmbedderFactory.java +++ b/maven.embedder/src/org/netbeans/modules/maven/embedder/EmbedderFactory.java @@ -52,7 +52,6 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.UnknownRepositoryLayoutException; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.repository.ArtifactRepositoryFactory; import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; @@ -61,6 +60,7 @@ import org.codehaus.plexus.classworlds.ClassWorld; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import java.util.prefs.Preferences; +import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; import org.apache.maven.model.Model; import org.apache.maven.model.building.DefaultModelBuildingRequest; import org.apache.maven.model.building.ModelBuilder; @@ -346,16 +346,11 @@ } public static ArtifactRepository createRemoteRepository(MavenEmbedder embedder, String url, String id) { - try { - ArtifactRepositoryFactory fact = embedder.lookupComponent(ArtifactRepositoryFactory.class); - assert fact!=null : "ArtifactRepositoryFactory component not found in maven"; - ArtifactRepositoryPolicy snapshotsPolicy = new ArtifactRepositoryPolicy(true, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN); - ArtifactRepositoryPolicy releasesPolicy = new ArtifactRepositoryPolicy(true, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN); - return fact.createArtifactRepository(id, url, ArtifactRepositoryFactory.DEFAULT_LAYOUT_ID, snapshotsPolicy, releasesPolicy); - } catch (UnknownRepositoryLayoutException ex) { - Exceptions.printStackTrace(ex); - } - return null; + ArtifactRepositoryFactory fact = embedder.lookupComponent(ArtifactRepositoryFactory.class); + assert fact!=null : "ArtifactRepositoryFactory component not found in maven"; + ArtifactRepositoryPolicy snapshotsPolicy = new ArtifactRepositoryPolicy(true, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN); + ArtifactRepositoryPolicy releasesPolicy = new ArtifactRepositoryPolicy(true, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN); + return fact.createArtifactRepository(id, url, new DefaultRepositoryLayout(), snapshotsPolicy, releasesPolicy); } /** diff --git a/maven/nbproject/project.xml b/maven/nbproject/project.xml --- a/maven/nbproject/project.xml +++ b/maven/nbproject/project.xml @@ -82,7 +82,7 @@ 1 - 1.25 + 1.35
diff --git a/maven/src/org/netbeans/modules/maven/queries/MavenFileOwnerQueryImpl.java b/maven/src/org/netbeans/modules/maven/queries/MavenFileOwnerQueryImpl.java --- a/maven/src/org/netbeans/modules/maven/queries/MavenFileOwnerQueryImpl.java +++ b/maven/src/org/netbeans/modules/maven/queries/MavenFileOwnerQueryImpl.java @@ -54,6 +54,7 @@ import java.util.prefs.Preferences; import javax.swing.event.ChangeListener; import org.apache.maven.project.MavenProject; +import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectManager; import org.netbeans.modules.maven.NbMavenProjectImpl; @@ -149,9 +150,13 @@ } return null; } - - private Project getOwner(File file) { - LOG.log(Level.FINER, "Looking for owner of {0}", file); + + /** + * Utility method to identify a file which might be an artifact in the local repository. + * @param file a putative artifact + * @return its coordinates (groupId/artifactId/version), or null if it cannot be identified + */ + static @CheckForNull String[] findCoordinates(File file) { String nm = file.getName(); // commons-math-2.1.jar File parentVer = file.getParentFile(); // ~/.m2/repository/org/apache/commons/commons-math/2.1 if (parentVer != null) { @@ -163,16 +168,14 @@ File parentGroup = parentArt.getParentFile(); // ~/.m2/repository/org/apache/commons if (parentGroup != null) { // Split rest into separate method, to avoid linking EmbedderFactory unless and until needed. - return getArtifactOwner(parentGroup, artifactID, version); + return findCoordinates(parentGroup, artifactID, version); } } } } return null; } - - private Project getArtifactOwner(File parentGroup, String artifactID, String version) { - LOG.log(Level.FINER, "Checking {0} / {1} / {2}", new Object[] {parentGroup, artifactID, version}); + private static @CheckForNull String[] findCoordinates(File parentGroup, String artifactID, String version) { File repo = new File(EmbedderFactory.getProjectEmbedder().getLocalRepository().getBasedir()); // ~/.m2/repository String repoS = repo.getAbsolutePath(); if (!repoS.endsWith(File.separator)) { @@ -184,51 +187,62 @@ } if (parentGroupS.startsWith(repoS)) { String groupID = parentGroupS.substring(repoS.length()).replace(File.separatorChar, '.'); // org.apache.commons - String key = groupID + ':' + artifactID; // org.apache.commons:commons-math - String ownerURI = prefs().get(key, null); - if (ownerURI != null) { - boolean stale = true; - try { - FileObject projectDir = URLMapper.findFileObject(new URI(ownerURI).toURL()); - if (projectDir != null && projectDir.isFolder()) { - Project p = ProjectManager.getDefault().findProject(projectDir); - if (p != null) { - NbMavenProjectImpl mp = p.getLookup().lookup(NbMavenProjectImpl.class); - if (mp != null) { - MavenProject model = mp.getOriginalMavenProject(); - if (model.getGroupId().equals(groupID) && model.getArtifactId().equals(artifactID)) { - if (model.getVersion().equals(version)) { - LOG.log(Level.FINE, "Found match {0}", p); - return p; - } else { - LOG.log(Level.FINE, "Mismatch on version {0} in {1}", new Object[] {model.getVersion(), ownerURI}); - stale = false; // we merely remembered another version - } + return new String[] {groupID, artifactID, version}; + } else { + return null; + } + } + + private Project getOwner(File file) { + LOG.log(Level.FINER, "Looking for owner of {0}", file); + String[] coordinates = findCoordinates(file); + if (coordinates == null) { + LOG.log(Level.FINE, "{0} not an artifact in local repo", file); + return null; + } + LOG.log(Level.FINER, "Checking {0} / {1} / {2}", coordinates); + String key = coordinates[0] + ':' + coordinates[1]; // org.apache.commons:commons-math + String ownerURI = prefs().get(key, null); + if (ownerURI != null) { + boolean stale = true; + try { + FileObject projectDir = URLMapper.findFileObject(new URI(ownerURI).toURL()); + if (projectDir != null && projectDir.isFolder()) { + Project p = ProjectManager.getDefault().findProject(projectDir); + if (p != null) { + NbMavenProjectImpl mp = p.getLookup().lookup(NbMavenProjectImpl.class); + if (mp != null) { + MavenProject model = mp.getOriginalMavenProject(); + if (model.getGroupId().equals(coordinates[0]) && model.getArtifactId().equals(coordinates[1])) { + if (model.getVersion().equals(coordinates[2])) { + LOG.log(Level.FINE, "Found match {0}", p); + return p; } else { - LOG.log(Level.FINE, "Mismatch on group and/or artifact ID in {0}", ownerURI); + LOG.log(Level.FINE, "Mismatch on version {0} in {1}", new Object[] {model.getVersion(), ownerURI}); + stale = false; // we merely remembered another version } } else { - LOG.log(Level.FINE, "Not a Maven project {0} in {1}", new Object[] {p, ownerURI}); + LOG.log(Level.FINE, "Mismatch on group and/or artifact ID in {0}", ownerURI); } } else { - LOG.log(Level.FINE, "No such project in {0}", ownerURI); + LOG.log(Level.FINE, "Not a Maven project {0} in {1}", new Object[] {p, ownerURI}); } } else { - LOG.log(Level.FINE, "No such folder {0}", ownerURI); + LOG.log(Level.FINE, "No such project in {0}", ownerURI); } - } catch (IOException x) { - LOG.log(Level.FINE, "Could not load project in " + ownerURI, x); - } catch (URISyntaxException x) { - LOG.log(Level.INFO, null, x); + } else { + LOG.log(Level.FINE, "No such folder {0}", ownerURI); } - if (stale) { - prefs().remove(key); // stale - } - } else { - LOG.log(Level.FINE, "No known owner for {0}", key); + } catch (IOException x) { + LOG.log(Level.FINE, "Could not load project in " + ownerURI, x); + } catch (URISyntaxException x) { + LOG.log(Level.INFO, null, x); + } + if (stale) { + prefs().remove(key); // stale } } else { - LOG.log(Level.FINE, "Not in local repo {0}", repoS); + LOG.log(Level.FINE, "No known owner for {0}", key); } return null; } diff --git a/maven/src/org/netbeans/modules/maven/queries/MavenSourceJavadocAttacher.java b/maven/src/org/netbeans/modules/maven/queries/MavenSourceJavadocAttacher.java new file mode 100644 --- /dev/null +++ b/maven/src/org/netbeans/modules/maven/queries/MavenSourceJavadocAttacher.java @@ -0,0 +1,114 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development and + * Distribution License("CDDL") (collectively, 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-gplv2.html or + * nbbuild/licenses/CDDL-GPL-2-CP. See the License for the specific language + * governing permissions and limitations under the License. When distributing + * the software, include this License Header Notice in each file and include + * the License file at nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided by + * Oracle in the GPL Version 2 section of the License file that accompanied + * this code. If applicable, add the following below the License Header, with + * the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you do not indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to its + * licensees as provided above. However, if you add GPL Version 2 code and + * therefore, elected the GPL Version 2 license, then the option applies only + * if the new code is made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.maven.queries; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.AbstractArtifactResolutionException; +import org.netbeans.api.progress.aggregate.AggregateProgressFactory; +import org.netbeans.api.progress.aggregate.AggregateProgressHandle; +import org.netbeans.api.progress.aggregate.ProgressContributor; +import org.netbeans.modules.maven.embedder.EmbedderFactory; +import org.netbeans.modules.maven.embedder.MavenEmbedder; +import org.netbeans.modules.maven.embedder.exec.ProgressTransferListener; +import org.netbeans.modules.maven.indexer.api.RepositoryInfo; +import org.netbeans.modules.maven.indexer.api.RepositoryPreferences; +import org.netbeans.spi.java.queries.SourceJavadocAttacherImplementation; +import org.openide.filesystems.FileUtil; +import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; + +@ServiceProvider(service=SourceJavadocAttacherImplementation.class, position=200) +public class MavenSourceJavadocAttacher implements SourceJavadocAttacherImplementation { + + @Override public Result attachSources(URL root) throws IOException { + return attach(root, false); + } + + @Override public Result attachJavadoc(URL root) throws IOException { + return attach(root, true); + } + + @Messages({"# {0} - artifact ID", "attaching=Attaching {0}"}) + private Result attach(URL root, boolean javadoc) throws IOException { + File file = FileUtil.archiveOrDirForURL(root); + if (file == null) { + return Result.UNSUPPORTED; + } + String[] coordinates = MavenFileOwnerQueryImpl.findCoordinates(file); + if (coordinates == null) { + return Result.UNSUPPORTED; + } + MavenEmbedder online = EmbedderFactory.getOnlineEmbedder(); + Artifact art = online.createArtifactWithClassifier(coordinates[0], coordinates[1], coordinates[2], "jar", javadoc ? "javadoc" : "sources"); + AggregateProgressHandle hndl = AggregateProgressFactory.createHandle(Bundle.attaching(art.getId()), + new ProgressContributor[] {AggregateProgressFactory.createProgressContributor("attach")}, + ProgressTransferListener.cancellable(), null); + ProgressTransferListener.setAggregateHandle(hndl); + try { + hndl.start(); + List repos = new ArrayList(); + // XXX is there some way to determine from local metadata which remote repo it is from? (i.e. API to read _maven.repositories) + for (RepositoryInfo info : RepositoryPreferences.getInstance().getRepositoryInfos()) { + if (info.isRemoteDownloadable()) { + repos.add(EmbedderFactory.createRemoteRepository(online, info.getRepositoryUrl(), info.getId())); + } + } + online.resolve(art, repos, online.getLocalRepository()); + if (art.getFile().isFile()) { + return Result.ATTACHED; + } else { + return Result.CANCELED; + } + } catch (ThreadDeath d) { + return Result.CANCELED; + } catch (AbstractArtifactResolutionException x) { + return Result.CANCELED; + } finally { + hndl.finish(); + ProgressTransferListener.clearAggregateHandle(); + } + } + +} diff --git a/maven/test/unit/src/org/netbeans/modules/maven/queries/MavenFileOwnerQueryImplTest.java b/maven/test/unit/src/org/netbeans/modules/maven/queries/MavenFileOwnerQueryImplTest.java --- a/maven/test/unit/src/org/netbeans/modules/maven/queries/MavenFileOwnerQueryImplTest.java +++ b/maven/test/unit/src/org/netbeans/modules/maven/queries/MavenFileOwnerQueryImplTest.java @@ -43,6 +43,7 @@ package org.netbeans.modules.maven.queries; import java.io.File; +import java.util.Arrays; import org.netbeans.api.project.ProjectManager; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.maven.NbMavenProjectImpl; @@ -60,8 +61,12 @@ clearWorkDir(); } - protected @Override int timeOut() { - return 300000; + public void testFindCoordinates() throws Exception { + File repo = new File(EmbedderFactory.getProjectEmbedder().getLocalRepository().getBasedir()); + assertEquals("[test, prj, 1.0]", Arrays.toString(MavenFileOwnerQueryImpl.findCoordinates(new File(repo, "test/prj/1.0/prj-1.0.jar")))); + assertEquals("[my.test, prj, 1.0-SNAPSHOT]", Arrays.toString(MavenFileOwnerQueryImpl.findCoordinates(new File(repo, "my/test/prj/1.0-SNAPSHOT/prj-1.0-SNAPSHOT.pom")))); + assertEquals("null", Arrays.toString(MavenFileOwnerQueryImpl.findCoordinates(new File(repo, "test/prj/1.0")))); + assertEquals("null", Arrays.toString(MavenFileOwnerQueryImpl.findCoordinates(getWorkDir()))); } public void testMultipleVersions() throws Exception {