Index: ant/project/apichanges.xml diff -u ant/project/apichanges.xml:1.22 ant/project/apichanges.xml:1.15.4.6 --- ant/project/apichanges.xml:1.22 Tue Oct 9 11:34:39 2007 +++ ant/project/apichanges.xml Tue Jan 15 02:03:45 2008 @@ -104,7 +104,44 @@ - + + + + Support for project-specific libraries + + + + + +

+ A new XML schema + (http://www.netbeans.org/ns/ant-project-libraries/1), + several new API methods + (AntProjectHelper.getProjectLibrariesPropertyProvider, + AntProjectHelper.isSharableProject, + AntProjectHelper.getLibrariesLocation, + AntProjectHelper.setLibrariesLocation, + ProjectGenerator.createProject(FileObject, String, String), + ReferenceHelper.createForeignFileReferenceAsIs, + ReferenceHelper.createExtraForeignFileReferenceAsIs, + ReferenceHelper.copyLibrary, + ReferenceHelper.getLibraryChooserImportHandler, + ReferenceHelper.createLibraryReference, + ReferenceHelper.getProjectLibraryManager, + ReferenceHelper.findLibrary and + FileChooser.showRelativizeFilePathCustomizer) and + custom file chooser (FileChooser) + provide support for collocated library definitions + referred to from Ant-based projects. +

+
+ + + + + +
+ Support for adding external files owned by project Index: ant/project/manifest.mf diff -u ant/project/manifest.mf:1.22 ant/project/manifest.mf:1.19.4.7 --- ant/project/manifest.mf:1.22 Fri Nov 9 09:57:37 2007 +++ ant/project/manifest.mf Tue Jan 15 02:03:45 2008 @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.project.ant/1 -OpenIDE-Module-Specification-Version: 1.18 +OpenIDE-Module-Specification-Version: 1.19 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/project/ant/Bundle.properties OpenIDE-Module-Install: org/netbeans/modules/project/ant/AntProjectModule.class Index: ant/project/nbproject/project.xml diff -u ant/project/nbproject/project.xml:1.25 ant/project/nbproject/project.xml:1.21.2.10 --- ant/project/nbproject/project.xml:1.25 Tue Oct 16 13:38:33 2007 +++ ant/project/nbproject/project.xml Thu Dec 6 07:22:31 2007 @@ -47,11 +47,29 @@ org.netbeans.modules.project.ant + org.jdesktop.layout + + + + 1 + 1.5 + + + org.netbeans.api.progress 1 + + + + org.netbeans.modules.project.libraries + + + + 1 + 1.15 Index: ant/project/src/META-INF/services/org.netbeans.spi.project.libraries.ArealLibraryProvider diff -u /dev/null ant/project/src/META-INF/services/org.netbeans.spi.project.libraries.ArealLibraryProvider:1.1.2.1 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/src/META-INF/services/org.netbeans.spi.project.libraries.ArealLibraryProvider Thu Apr 26 18:54:13 2007 @@ -0,0 +1 @@ +org.netbeans.modules.project.ant.ProjectLibraryProvider Index: ant/project/src/META-INF/services/org.netbeans.spi.queries.SharabilityQueryImplementation diff -u /dev/null ant/project/src/META-INF/services/org.netbeans.spi.queries.SharabilityQueryImplementation:1.1.2.1 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/src/META-INF/services/org.netbeans.spi.queries.SharabilityQueryImplementation Fri Apr 27 23:01:56 2007 @@ -0,0 +1,2 @@ +org.netbeans.modules.project.ant.ProjectLibraryProvider$SharabilityQueryImpl +#position=50 Index: ant/project/src/org/netbeans/api/project/ant/FileChooser.java diff -u /dev/null ant/project/src/org/netbeans/api/project/ant/FileChooser.java:1.1.2.7 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/src/org/netbeans/api/project/ant/FileChooser.java Tue Jan 15 02:03:45 2008 @@ -0,0 +1,216 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * 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. + */ + +package org.netbeans.api.project.ant; + +import java.awt.Dialog; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import javax.swing.JFileChooser; +import org.netbeans.api.project.FileOwnerQuery; +import org.netbeans.api.project.Project; +import org.netbeans.api.project.libraries.LibraryManager; +import org.netbeans.modules.project.ant.FileChooserAccessory; +import org.netbeans.modules.project.ant.RelativizeFilePathCustomizer; +import org.netbeans.modules.project.ant.ProjectLibraryProvider; +import org.netbeans.spi.project.AuxiliaryConfiguration; +import org.netbeans.spi.project.support.ant.AntProjectHelper; +import org.openide.DialogDescriptor; +import org.openide.DialogDisplayer; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.util.NbBundle; + + +/** + * Custom file chooser allowing user to choose how a file will be referenced + * from a project - via absolute path, or relative path. Make sure you call + * {@link #getFiles} instead of {@link #getSelectedFiles} as it returns relative + * files and performs file copying if necessary. + * + * @author David Konecny + * @since org.netbeans.modules.project.ant/1 1.19 + */ +public final class FileChooser extends JFileChooser { + + private FileChooserAccessory accessory; + + /** + * Create chooser for given AntProjectHelper. Standard file chooser is shown + * if project is not sharable. + * + * @param helper ant project helper; cannot be null + * @param copyAllowed is file copying allowed + */ + public FileChooser(AntProjectHelper helper, boolean copyAllowed) { + super(); + LibraryManager lm = ProjectLibraryProvider.getProjectLibraryManager(helper); + if (lm != null) { + URL u = lm.getLocation(); + if (u != null) { + File libBase = new File(URI.create(u.toExternalForm())).getParentFile(); + accessory = new FileChooserAccessory(this, FileUtil.toFile(helper.getProjectDirectory()), + libBase, copyAllowed); + setAccessory(accessory); + } + } + } + + /** + * Create chooser for given base folder and shared libraries folder. + * + * @param baseFolder base folder to which all selected files will be relativized; + * can be null in which case regular file chooser is shown + * @param sharedLibrariesFolder providing shared libraries folder enables option + * of copying selected files there; can be null in which case copying option + * is disabled + */ + public FileChooser(File baseFolder, File sharedLibrariesFolder) { + super(); + if (baseFolder != null) { + accessory = new FileChooserAccessory(this, baseFolder, sharedLibrariesFolder, sharedLibrariesFolder != null); + setAccessory(accessory); + } + } + + /** + * Returns array of files selected. The difference from + * {@link #getSelectedFiles} is that depends on user's choice the files + * may be relative and they may have been copied to different location. + * + * @return array of files which may be relative to base folder this chooser + * was created for; e.g. project folder in case of AntProjectHelper + * constructor; never null; can be empty array + * @throws java.io.IOException any IO problem; for example during + * file copying + */ + public File[] getFiles() throws IOException { + if (accessory != null) { + accessory.copyFilesIfNecessary(); + if (accessory.isRelative()) { + return accessory.getFiles(); + } + } + if (isMultiSelectionEnabled()) { + return getSelectedFiles(); + } else { + if (getSelectedFile() != null) { + return new File[]{getSelectedFile()}; + } else { + return new File[0]; + } + } + } + + @Override + public void approveSelection() { + if (accessory != null && !accessory.canApprove()) { + return; + } + super.approveSelection(); + } + + /** + * Show UI allowing user to decide how the given file should be referenced, + * that is via absolute or relative path. Optionally UI can allow to copy + * file to shared libraries folder. + * + * @param fileToReference file in question + * @param projectArtifact any project artifact, e.g. project folder or project source etc. + * @param copyAllowed is file copying allowed + * @return possibly relative file; null if cancelled by user or project + * cannot be found for project artifact + * @throws java.io.IOException any IO failure during file copying + */ + public static File showRelativizeFilePathCustomizer(File fileToReference, FileObject projectArtifact, + boolean copyAllowed) throws IOException { + Project p = FileOwnerQuery.getOwner(projectArtifact); + if (p == null) { + return null; + } + AuxiliaryConfiguration aux = p.getLookup().lookup(AuxiliaryConfiguration.class); + assert aux != null : projectArtifact; + if (aux == null) { + return null; + } + File projFolder = FileUtil.toFile(p.getProjectDirectory()); + File libBase = ProjectLibraryProvider.getLibrariesLocation(aux, projFolder); + if (libBase != null) { + libBase = libBase.getParentFile(); + } + return showRelativizeFilePathCustomizer(fileToReference, projFolder, libBase, copyAllowed); + } + + /** + * Show UI allowing user to decide how the given file should be referenced, + * that is via absolute or relative path. Optionally UI can allow to copy + * file to shared libraries folder. + * + * @param fileToReference file in question + * @param baseFolder folder to relativize file against + * @param sharedLibrariesFolder optional shared libraries folder; can be null; + * if provided UI will allow user to copy given file there + * @param copyAllowed is file copying allowed + * @return possibly relative file; null if cancelled by user + * @throws java.io.IOException any IO failure during file copying + */ + private static File showRelativizeFilePathCustomizer(File fileToReference, File baseFolder, + File sharedLibrariesFolder, boolean copyAllowed) throws IOException { + RelativizeFilePathCustomizer panel = new RelativizeFilePathCustomizer( + fileToReference, baseFolder, sharedLibrariesFolder, copyAllowed); + DialogDescriptor descriptor = new DialogDescriptor (panel, + NbBundle.getMessage(RelativizeFilePathCustomizer.class, "RelativizeFilePathCustomizer.title")); + Dialog dlg = DialogDisplayer.getDefault().createDialog(descriptor); + try { + dlg.setVisible(true); + if (descriptor.getValue() == DialogDescriptor.OK_OPTION) { + return panel.getFile(); + } else { + return null; + } + } finally { + dlg.dispose(); + } + } + +} Index: ant/project/src/org/netbeans/modules/project/ant/Bundle.properties diff -u ant/project/src/org/netbeans/modules/project/ant/Bundle.properties:1.12 ant/project/src/org/netbeans/modules/project/ant/Bundle.properties:1.11.10.5 --- ant/project/src/org/netbeans/modules/project/ant/Bundle.properties:1.12 Wed Sep 26 14:15:16 2007 +++ ant/project/src/org/netbeans/modules/project/ant/Bundle.properties Thu Jan 10 00:08:45 2008 @@ -52,3 +52,15 @@ AntBasedProjectFactorySingleton.parseError=Error parsing {0}: {1} LBL_Incompatible_Xalan=An incompatible Xalan version has been detected on the IDE's classpath. The IDE cannot start. Please see http://wiki.netbeans.org/wiki/view/FaqXalanOnCP for more information. + +ProjectLibraryProvider.open_or_create=Open or Create +ProjectLibraryProvider.properties_files=*.properties Files +FileChooserAccessory.rbRelative.text=Use relative path: +FileChooserAccessory.rbCopy.text=Copy to shared libraries location: +FileChooserAccessory.rbAbsolute.text=Use absolute path: +FileChooserAccessory.warning1=Files can be only copied to the sharable libraries location ({0}) or its subfolder(s). +FileChooserAccessory.warning2=Are you sure you want to create folder {0} ? +FileChooserAccessory.warning3=Some of the files already exist in destination directory. Are you sure you want to override them? +FileChooserAccessory.warning4=Some of the selected files are folders. Are you sure you want to copy folder and recursively all its subfoders? +RelativizeFilePathCustomizer.title=Adding artifact to project +RelativizeFilePathCustomizer.jLabel1.text=How to reference file {0}? Index: ant/project/src/org/netbeans/modules/project/ant/FileChooserAccessory.form diff -u /dev/null ant/project/src/org/netbeans/modules/project/ant/FileChooserAccessory.form:1.1.2.1 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/src/org/netbeans/modules/project/ant/FileChooserAccessory.form Thu Dec 6 07:18:50 2007 @@ -0,0 +1,121 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Index: ant/project/src/org/netbeans/modules/project/ant/FileChooserAccessory.java diff -u /dev/null ant/project/src/org/netbeans/modules/project/ant/FileChooserAccessory.java:1.1.2.5 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/src/org/netbeans/modules/project/ant/FileChooserAccessory.java Mon Jan 14 02:33:48 2008 @@ -0,0 +1,417 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * 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. + */ + +package org.netbeans.modules.project.ant; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import javax.swing.JFileChooser; +import org.netbeans.spi.project.support.ant.PropertyUtils; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.util.NbBundle; + +/** + * Accessory allowing to choose how file is referenced from a project - relative + * or absolute. + * + *

The panel is used from two different places - FileChooser and + * RelativizeFilePathCustomizer. + * + * @author David Konecny + */ +public class FileChooserAccessory extends javax.swing.JPanel + implements ActionListener, PropertyChangeListener { + + private File baseFolder; + private final File sharedLibrariesFolder; + private boolean copyAllowed; + private JFileChooser chooser; + private List copiedRelativeFiles = null; + /** In RelativizeFilePathCustomizer scenario this property holds preselected file */ + private File usetThisFileInsteadOfOneFromChooser = null; + + /** + * Constructor for usage from RelativizeFilePathCustomizer. + */ + public FileChooserAccessory(File baseFolder, File sharedLibrariesFolder, boolean copyAllowed, File selectedFile) { + this(null, baseFolder, sharedLibrariesFolder, copyAllowed); + usetThisFileInsteadOfOneFromChooser = selectedFile; + enableAccessory(true); + update(Collections.singletonList(selectedFile)); + } + + /** + * Constructor for usage from FileChooser. + */ + public FileChooserAccessory(JFileChooser chooser, File baseFolder, File sharedLibrariesFolder, boolean copyAllowed) { + assert baseFolder != null; + assert !baseFolder.isFile(); + if (sharedLibrariesFolder != null) { + assert !sharedLibrariesFolder.isFile() : true; + if (!sharedLibrariesFolder.equals(FileUtil.normalizeFile(sharedLibrariesFolder))) { + throw new IllegalArgumentException("Parameter file was not "+ // NOI18N + "normalized. Was "+sharedLibrariesFolder+" instead of "+ + FileUtil.normalizeFile(sharedLibrariesFolder)); // NOI18N + } + } + this.baseFolder = baseFolder; + this.sharedLibrariesFolder = sharedLibrariesFolder; + this.copyAllowed = copyAllowed && sharedLibrariesFolder != null; + this.chooser = chooser; + initComponents(); + //copyPanel.setVisible(copyAllowed); + rbCopy.addActionListener(this); + rbRelative.addActionListener(this); + rbAbsolute.addActionListener(this); + if (chooser != null) { + chooser.addPropertyChangeListener(this); + } + if (sharedLibrariesFolder != null) { + copyTo.setText(sharedLibrariesFolder.getAbsolutePath()); + } + enableAccessory(false); + } + + public File[] getFiles() { + assert isRelative(); + if (isCopy()) { + return copiedRelativeFiles.toArray(new File[copiedRelativeFiles.size()]); + } else { + List files = Arrays.asList(getSelectedFiles()); + List l = getRelativeFiles(files); + return l.toArray(new File[l.size()]); + } + } + + public boolean canApprove() { + if (!isCopy()) { + return true; + } + File f = FileUtil.normalizeFile(new File(copyTo.getText())); + if (!f.getPath().equals(sharedLibrariesFolder.getPath()) && + !(f.getPath()).startsWith(sharedLibrariesFolder.getPath()+File.separatorChar)) { + DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message( + MessageFormat.format(NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.warning1"), // NOI18N + new Object[] {sharedLibrariesFolder.getPath()}))); + + return false; + } + final File[] files = getSelectedFiles(); + if (f.exists()) { + for (File file : files) { + File testFile = new File(f, file.getName()); + if (testFile.exists()) { + if (NotifyDescriptor.YES_OPTION != DialogDisplayer.getDefault().notify(new NotifyDescriptor.Confirmation( + NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.warning3")))) { + return false; + } + break; + } + } + } else { + if (NotifyDescriptor.YES_OPTION != DialogDisplayer.getDefault().notify(new NotifyDescriptor.Confirmation( + MessageFormat.format(NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.warning2"), // NOI18N + new Object[] {f.getPath()})))) { + return false; + } + } + for (File file : files) { + if (file.isDirectory()) { + if (NotifyDescriptor.YES_OPTION != DialogDisplayer.getDefault().notify(new NotifyDescriptor.Confirmation( + NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.warning4")))) { // NOI18N + return false; + } + break; + } + } + + return true; + } + + public void copyFilesIfNecessary() throws IOException { + if (!isCopy()) { + return; + } + File f = FileUtil.normalizeFile(new File(copyTo.getText())); + FileUtil.createFolder(f); + FileObject fo = FileUtil.toFileObject(FileUtil.normalizeFile(f)); + List selectedFiles = Arrays.asList(getSelectedFiles()); + copyFiles(selectedFiles, fo); + } + + private void enableAccessory(boolean enable) { + rbRelative.setEnabled(enable); + rbCopy.setEnabled(enable && copyAllowed); + rbAbsolute.setEnabled(enable); + copyTo.setEnabled(enable); + copyTo.setEditable(enable && rbCopy.isSelected()); + } + + private File[] getSelectedFiles() { + if (usetThisFileInsteadOfOneFromChooser != null) { + return new File[]{usetThisFileInsteadOfOneFromChooser}; + } + if (chooser.isMultiSelectionEnabled()) { + return chooser.getSelectedFiles(); + } else { + if (chooser.getSelectedFile() != null) { + return new File[] { chooser.getSelectedFile() }; + } else { + return new File[0]; + } + } + } + + public boolean isRelative() { + return (rbRelative.isEnabled() && rbRelative.isSelected()) || isCopy(); + } + + private boolean isCopy() { + return rbCopy.isEnabled() && rbCopy.isSelected(); + } + + public void actionPerformed(ActionEvent e) { + copyTo.setEditable(e.getSource() == rbCopy); + } + + public void propertyChange(PropertyChangeEvent e) { + if (!(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals(e.getPropertyName()) || + JFileChooser.SELECTED_FILES_CHANGED_PROPERTY.equals(e.getPropertyName()))) { + return; + } + File[] files = getSelectedFiles(); + enableAccessory(files.length != 0); + update(Arrays.asList(files)); + } + + private void update(List files) { + StringBuffer absolute = new StringBuffer(); + StringBuffer relative = new StringBuffer(); + StringBuffer copy = new StringBuffer(); + boolean isRelative = true; + for (File file : files) { + if (absolute.length() != 0) { + absolute.append(", "); + relative.append(", "); + if (file.getParentFile() != null && file.getParentFile().getParentFile() != null && + file.getParentFile().getName().length() > 0) { + copy.setLength(0); + copy.append("/"+file.getParentFile().getName()); + } + } else { + // first file: + if (file.getParentFile() != null && file.getParentFile().getParentFile() != null && + file.getParentFile().getName().length() > 0) { + copy.append("/"+file.getParentFile().getName()+"/"+file.getName()); + } else { + copy.append("/"); + } + } + absolute.append(file.getAbsolutePath()); + String s = PropertyUtils.relativizeFile(baseFolder, file); + if (s == null) { + isRelative = false; + } + relative.append(s); + } + rbRelative.setEnabled(isRelative && rbRelative.isEnabled()); + relativePath.setText(relative.toString()); + relativePath.setCaretPosition(0); + relativePath.setToolTipText(relative.toString()); + if (!isRelative) { + relativePath.setText(""); + relativePath.setToolTipText(""); + } + absolutePath.setText(absolute.toString()); + absolutePath.setCaretPosition(0); + absolutePath.setToolTipText(absolute.toString()); + } + + private List getRelativeFiles(List files) { + List fs = new ArrayList(); + for (File file : files) { + String s = PropertyUtils.relativizeFile(baseFolder, file); + if (s != null) { + fs.add(new File(s)); + } + } + return fs; + } + + private void copyFiles(List files, FileObject newRoot) throws IOException { + List fs = new ArrayList(); + for (File file : files) { + FileObject fo = FileUtil.toFileObject(file); + FileObject newFO; + if (fo.isFolder()) { + newFO = copyFolderRecursively(fo, newRoot); + } else { + FileObject foExists = newRoot.getFileObject(fo.getName(), fo.getExt()); + if (foExists != null) { + foExists.delete(); + } + newFO = FileUtil.copyFile(fo, newRoot, fo.getName(), fo.getExt()); + } + fs.add(FileUtil.toFile(newFO)); + } + copiedRelativeFiles = getRelativeFiles(fs); + } + + public static FileObject copyFolderRecursively(FileObject sourceFolder, FileObject destination) throws IOException { + assert sourceFolder.isFolder() : sourceFolder; + assert destination.isFolder() : destination; + FileObject destinationSubFolder = destination.getFileObject(sourceFolder.getName()); + if (destinationSubFolder == null) { + destinationSubFolder = destination.createFolder(sourceFolder.getName()); + } + for (FileObject fo : sourceFolder.getChildren()) { + if (fo.isFolder()) { + copyFolderRecursively(fo, destinationSubFolder); + } else { + FileObject foExists = destinationSubFolder.getFileObject(fo.getName(), fo.getExt()); + if (foExists != null) { + foExists.delete(); + } + FileObject newFO = FileUtil.copyFile(fo, destinationSubFolder, fo.getName(), fo.getExt()); + } + } + return destinationSubFolder; + } + + /** 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. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonGroup1 = new javax.swing.ButtonGroup(); + rbRelative = new javax.swing.JRadioButton(); + relativePath = new javax.swing.JTextField(); + rbCopy = new javax.swing.JRadioButton(); + copyTo = new javax.swing.JTextField(); + rbAbsolute = new javax.swing.JRadioButton(); + absolutePath = new javax.swing.JTextField(); + + setMinimumSize(new java.awt.Dimension(250, 139)); + setPreferredSize(new java.awt.Dimension(250, 139)); + + buttonGroup1.add(rbRelative); + rbRelative.setSelected(true); + rbRelative.setText(org.openide.util.NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.rbRelative.text")); // NOI18N + + relativePath.setEditable(false); + relativePath.setText(null); + + buttonGroup1.add(rbCopy); + rbCopy.setText(org.openide.util.NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.rbCopy.text")); // NOI18N + + copyTo.setEditable(false); + copyTo.setText(null); + + buttonGroup1.add(rbAbsolute); + rbAbsolute.setText(org.openide.util.NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.rbAbsolute.text")); // NOI18N + + absolutePath.setEditable(false); + absolutePath.setText(null); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(rbRelative) + .add(layout.createSequentialGroup() + .add(21, 21, 21) + .add(relativePath, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 219, Short.MAX_VALUE)) + .add(rbCopy, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 240, Short.MAX_VALUE) + .add(layout.createSequentialGroup() + .add(21, 21, 21) + .add(copyTo, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 219, Short.MAX_VALUE)) + .add(rbAbsolute) + .add(layout.createSequentialGroup() + .add(21, 21, 21) + .add(absolutePath, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 219, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(rbRelative) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(relativePath, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(rbCopy) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(copyTo, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(rbAbsolute) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(absolutePath, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextField absolutePath; + private javax.swing.ButtonGroup buttonGroup1; + private javax.swing.JTextField copyTo; + private javax.swing.JRadioButton rbAbsolute; + private javax.swing.JRadioButton rbCopy; + private javax.swing.JRadioButton rbRelative; + private javax.swing.JTextField relativePath; + // End of variables declaration//GEN-END:variables + +} Index: ant/project/src/org/netbeans/modules/project/ant/FileOwnerCollocationQueryImpl.java diff -u /dev/null ant/project/src/org/netbeans/modules/project/ant/FileOwnerCollocationQueryImpl.java:1.1.2.2 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/src/org/netbeans/modules/project/ant/FileOwnerCollocationQueryImpl.java Tue Jan 15 00:49:34 2008 @@ -0,0 +1,99 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * 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. + */ + +package org.netbeans.modules.project.ant; + +import java.io.File; +import java.net.URI; +import org.netbeans.api.project.FileOwnerQuery; +import org.netbeans.api.project.Project; +import org.netbeans.spi.queries.CollocationQueryImplementation; + +/** + * A CollocationQueryImplementation implementation that collocates files based on + * projects they are in. + * @author Milos Kleint + * @since org.netbeans.modules.project.ant/1 1.18 + */ +public class FileOwnerCollocationQueryImpl implements CollocationQueryImplementation { + + + /** Creates a new instance of FileOwnerCollocationQueryImpl */ + public FileOwnerCollocationQueryImpl() { + } + + public File findRoot(File file) { + File f = file; + URI uri = f.toURI(); + Project prj = FileOwnerQuery.getOwner(uri); + if (prj == null) { + return null; + } + while (prj != null && f != null) { + f = f.getParentFile(); + if (f != null) { + prj = FileOwnerQuery.getOwner(f.toURI()); + } else { + prj = null; + } + } + return f; + + } + + public boolean areCollocated(File file1, File file2) { + File root = findRoot (file1); + boolean first = true; + if (root == null) { + root = findRoot (file2); + first = false; + } + if (root != null) { + String rootpath = root.getAbsolutePath() + File.separator; + String check = (first ? file2.getAbsolutePath() : file1.getAbsolutePath()) + File.separator; + return check.startsWith(rootpath); + } + return false; + } + + + +} Index: ant/project/src/org/netbeans/modules/project/ant/ProjectLibraryProvider.java diff -u /dev/null ant/project/src/org/netbeans/modules/project/ant/ProjectLibraryProvider.java:1.1.2.33 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/src/org/netbeans/modules/project/ant/ProjectLibraryProvider.java Tue Jan 15 00:49:34 2008 @@ -0,0 +1,941 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.modules.project.ant; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.swing.JFileChooser; +import javax.swing.event.ChangeListener; +import javax.swing.filechooser.FileFilter; +import org.netbeans.api.project.Project; +import org.netbeans.api.project.ProjectManager; +import org.netbeans.api.project.libraries.Library; +import org.netbeans.api.project.libraries.LibraryManager; +import org.netbeans.api.project.ui.OpenProjects; +import org.netbeans.api.queries.SharabilityQuery; +import org.netbeans.spi.project.AuxiliaryConfiguration; +import org.netbeans.spi.project.libraries.ArealLibraryProvider; +import org.netbeans.spi.project.libraries.LibraryImplementation; +import org.netbeans.spi.project.libraries.LibraryProvider; +import org.netbeans.spi.project.libraries.LibraryStorageArea; +import org.netbeans.spi.project.libraries.support.LibrariesSupport; +import org.netbeans.spi.project.support.ant.AntProjectEvent; +import org.netbeans.spi.project.support.ant.AntProjectHelper; +import org.netbeans.spi.project.support.ant.AntProjectListener; +import org.netbeans.spi.project.support.ant.EditableProperties; +import org.netbeans.spi.project.support.ant.PropertyProvider; +import org.netbeans.spi.project.support.ant.PropertyUtils; +import org.netbeans.spi.queries.SharabilityQueryImplementation; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.filesystems.URLMapper; +import org.openide.util.ChangeSupport; +import org.openide.util.Exceptions; +import org.openide.util.Mutex; +import org.openide.util.MutexException; +import org.openide.util.NbBundle; +import org.openide.util.NbCollections; +import org.openide.util.RequestProcessor; +import org.openide.util.Utilities; +import org.openide.util.WeakListeners; +import org.openide.xml.XMLUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Supplier of libraries declared in open projects. + * @see "issue #44035" + */ +public class ProjectLibraryProvider implements ArealLibraryProvider, PropertyChangeListener, AntProjectListener { + + private static final String NAMESPACE = "http://www.netbeans.org/ns/ant-project-libraries/1"; // NOI18N + private static final String EL_LIBRARIES = "libraries"; // NOI18N + private static final String EL_DEFINITIONS = "definitions"; // NOI18N + + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private AntProjectListener apl; + + public static ProjectLibraryProvider INSTANCE; + /** + * Default constructor for lookup. + */ + public ProjectLibraryProvider() { + INSTANCE = this; + } + + public Class areaType() { + return ProjectLibraryArea.class; + } + + public Class libraryType() { + return ProjectLibraryImplementation.class; + } + + @Override + public String toString() { + return "ProjectLibraryProvider"; // NOI18N + } + + // ---- management of areas ---- + + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + public Set getOpenAreas() { + synchronized (this) { // lazy init of OpenProjects-related stuff is better for unit testing + if (apl == null) { + apl = WeakListeners.create(AntProjectListener.class, this, null); + OpenProjects.getDefault().addPropertyChangeListener(WeakListeners.propertyChange(this, OpenProjects.getDefault())); + } + } + Set areas = new HashSet(); + for (Project p : OpenProjects.getDefault().getOpenProjects()) { + AntProjectHelper helper = AntBasedProjectFactorySingleton.getHelperFor(p); + if (helper == null) { + // Not an Ant-based project; ignore. + continue; + } + helper.removeAntProjectListener(apl); + helper.addAntProjectListener(apl); + Definitions def = findDefinitions(helper); + if (def != null) { + areas.add(new ProjectLibraryArea(def.mainPropertiesFile)); + } + } + return areas; + } + + public ProjectLibraryArea createArea() { + JFileChooser jfc = new JFileChooser(); + jfc.setApproveButtonText(NbBundle.getMessage(ProjectLibraryProvider.class, "ProjectLibraryProvider.open_or_create")); + FileFilter filter = new FileFilter() { + public boolean accept(File f) { + return f.isDirectory() || (f.getName().endsWith(".properties") && !f.getName().endsWith("-private.properties")); // NOI18N + } + public String getDescription() { + return NbBundle.getMessage(ProjectLibraryProvider.class, "ProjectLibraryProvider.properties_files"); + } + }; + jfc.setFileFilter(filter); + FileUtil.preventFileChooserSymlinkTraversal(jfc, null); // XXX remember last-selected dir + while (jfc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { + File f = jfc.getSelectedFile(); + if (filter.accept(f)) { + return new ProjectLibraryArea(f); + } + // Else bad filename, reopen dialog. XXX would be better to just disable OK button, but not sure how...? + } + return null; + } + + public ProjectLibraryArea loadArea(URL location) { + if (location.getProtocol().equals("file") && location.getPath().endsWith(".properties")) { // NOI18N + try { + return new ProjectLibraryArea(new File(location.toURI())); + } catch (URISyntaxException x) { + Exceptions.printStackTrace(x); + } + } + return null; + } + + public void propertyChange(PropertyChangeEvent ev) { + if (OpenProjects.PROPERTY_OPEN_PROJECTS.equals(ev.getPropertyName())) { + pcs.firePropertyChange(ArealLibraryProvider.PROP_OPEN_AREAS, null, null); + } + } + + public void configurationXmlChanged(AntProjectEvent ev) { + pcs.firePropertyChange(ArealLibraryProvider.PROP_OPEN_AREAS, null, null); + } + + public void propertiesChanged(AntProjectEvent ev) {} + + // ---- management of libraries ---- + + private boolean listening = true; + private final Map> providers = new HashMap>(); + + private final class LP implements LibraryProvider, FileChangeSupportListener { + + private final ProjectLibraryArea area; + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private final Map libraries; + + LP(ProjectLibraryArea area) { + this.area = area; + libraries = calculate(area); + Definitions defs = new Definitions(area.mainPropertiesFile); + FileChangeSupport.DEFAULT.addListener(this, defs.mainPropertiesFile); + FileChangeSupport.DEFAULT.addListener(this, defs.privatePropertiesFile); + } + + public synchronized ProjectLibraryImplementation[] getLibraries() { + return libraries.values().toArray(new ProjectLibraryImplementation[libraries.size()]); + } + + ProjectLibraryImplementation getLibrary(String name) { + return libraries.get(name); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + public void fileCreated(FileChangeSupportEvent event) { + recalculate(); + } + + public void fileDeleted(FileChangeSupportEvent event) { + recalculate(); + } + + public void fileModified(FileChangeSupportEvent event) { + recalculate(); + } + + private synchronized void recalculate() { + if (delta(libraries, calculate(area))) { + pcs.firePropertyChange(LibraryProvider.PROP_LIBRARIES, null, null); + } + } + + } + + public synchronized LP getLibraries(ProjectLibraryArea area) { + Reference rlp = providers.get(area); + LP lp = rlp != null ? rlp.get() : null; + if (lp == null) { + lp = new LP(area); + providers.put(area, new WeakReference(lp)); + } + return lp; + } + + public ProjectLibraryImplementation createLibrary(String type, String name, ProjectLibraryArea area, Map> contents) throws IOException { + File f = area.mainPropertiesFile; + assert listening; + listening = false; + try { + if (type.equals("j2se")) { // NOI18N + replaceProperty(f, true, "libs." + name + ".classpath", ""); // NOI18N + } else { + replaceProperty(f, false, "libs." + name + ".type", type); // NOI18N + } + } finally { + listening = true; + } + LP lp = getLibraries(area); + lp.recalculate(); + ProjectLibraryImplementation impl = lp.getLibrary(name); + assert impl != null : name + " not found in " + f; + for (Map.Entry> entry : contents.entrySet()) { + impl.setContent(entry.getKey(), entry.getValue()); + } + return impl; + } + + public void remove(ProjectLibraryImplementation pli) throws IOException { + String prefix = "libs." + pli.name + "."; // NOI18N + // XXX run atomically to fire changes just once: + for (File f : new File[] {pli.mainPropertiesFile, pli.privatePropertiesFile}) { + for (String k : loadProperties(f).keySet()) { + if (k.startsWith(prefix)) { + replaceProperty(f, false, k); + } + } + } + } + + /** one definitions entry */ + private static final class Definitions { + /** may or may not exist; in case you need to listen to it */ + final File mainPropertiesFile; + /** similar to {@link #mainPropertiesFile} but for *-private.properties; null if main is not *.properties */ + final File privatePropertiesFile; + private Map properties; + Definitions(File mainPropertiesFile) { + this.mainPropertiesFile = mainPropertiesFile; + String suffix = ".properties"; // NOI18N + String name = mainPropertiesFile.getName(); + if (name.endsWith(suffix)) { + privatePropertiesFile = new File(mainPropertiesFile.getParentFile(), name.substring(0, name.length() - suffix.length()) + "-private" + suffix); // NOI18N + } else { + privatePropertiesFile = null; + } + } + /** with ${base} resolved according to resolveBase; may be empty or have junk defs */ + synchronized Map properties(boolean resolveBase) { + if (properties == null) { + properties = new HashMap(); + String basedir = mainPropertiesFile.getParent(); + for (Map.Entry entry : loadProperties(mainPropertiesFile).entrySet()) { + String value = entry.getValue(); + if (resolveBase) { + value = value.replace("${base}", basedir); // NOI18N + } + properties.put(entry.getKey(), value.replace('/', File.separatorChar)); + } + if (privatePropertiesFile != null) { + for (Map.Entry entry : loadProperties(privatePropertiesFile).entrySet()) { + String value = entry.getValue(); + if (resolveBase) { + value = value.replace("${base}", basedir); // NOI18N + } + properties.put(entry.getKey(), value.replace('/', File.separatorChar)); + } + } + } + return properties; + } + } + + private static Definitions findDefinitions(AntProjectHelper helper) { + String text = getLibrariesLocationText(helper.createAuxiliaryConfiguration()); + if (text != null) { + File mainPropertiesFile = helper.resolveFile(text); + if (mainPropertiesFile.getName().endsWith(".properties")) { // NOI18N + return new Definitions(mainPropertiesFile); + } + } + return null; + } + + public static File getLibrariesLocation(AuxiliaryConfiguration aux, File projectFolder) { + String text = getLibrariesLocationText(aux); + if (text != null) { + return PropertyUtils.resolveFile(projectFolder, text); + } + return null; + } + + /** + * Returns libraries location as text. + */ + public static String getLibrariesLocationText(AuxiliaryConfiguration aux) { + Element libraries = aux.getConfigurationFragment(EL_LIBRARIES, NAMESPACE, true); + if (libraries != null) { + for (Element definitions : Util.findSubElements(libraries)) { + assert definitions.getLocalName().equals(EL_DEFINITIONS) : definitions; + String text = Util.findText(definitions); + assert text != null : aux; + return text; + } + } + return null; + } + + private static Map loadProperties(File f) { + if (!f.isFile()) { + return Collections.emptyMap(); + } + Properties p = new Properties(); + try { + InputStream is = new FileInputStream(f); + try { + p.load(is); + } finally { + is.close(); + } + return NbCollections.checkedMapByFilter(p, String.class, String.class, true); + } catch (IOException x) { + Exceptions.attachMessage(x, "Loading: " + f); // NOI18N + Exceptions.printStackTrace(x); + return Collections.emptyMap(); + } + } + + private static final Pattern LIBS_LINE = Pattern.compile("libs\\.([^.]+)\\.([^.]+)"); // NOI18N + private static Map calculate(ProjectLibraryArea area) { + Map libs = new HashMap(); + Definitions def = new Definitions(area.mainPropertiesFile); + Map> data = new HashMap>(); + for (Map.Entry entry : def.properties(false).entrySet()) { + Matcher match = LIBS_LINE.matcher(entry.getKey()); + if (!match.matches()) { + continue; + } + String name = match.group(1); + Map subdata = data.get(name); + if (subdata == null) { + subdata = new HashMap(); + data.put(name, subdata); + } + subdata.put(match.group(2), entry.getValue()); + } + for (Map.Entry> entry : data.entrySet()) { + String name = entry.getKey(); + String type = "j2se"; // NOI18N + String description = null; + Map> contents = new HashMap>(); + for (Map.Entry subentry : entry.getValue().entrySet()) { + String k = subentry.getKey(); + if (k.equals("type")) { // NOI18N + type = subentry.getValue(); + } else if (k.equals("name")) { // NOI18N + // XXX currently overriding display name is not supported + } else if (k.equals("description")) { // NOI18N + description = subentry.getValue(); + } else { + String[] path = PropertyUtils.tokenizePath(subentry.getValue()); + List volume = new ArrayList(path.length); + for (String component : path) { + File f = new File(component.replace('/', File.separatorChar).replace('\\', File.separatorChar).replace("${base}"+File.separatorChar, "")); + File normalizedFile = FileUtil.normalizeFile(new File(component.replace('/', File.separatorChar).replace('\\', File.separatorChar).replace("${base}", area.mainPropertiesFile.getParent()))); + try { + URL u = LibrariesSupport.convertFileToURL(f); + if (FileUtil.isArchiveFile(normalizedFile.toURI().toURL())) { + u = FileUtil.getArchiveRoot(u); + } else if (!u.toExternalForm().endsWith("/")) { + u = new URL(u.toExternalForm() + "/"); + } + volume.add(u); + } catch (MalformedURLException x) { + Exceptions.printStackTrace(x); + } + } + contents.put(k, volume); + } + } + libs.put(name, new ProjectLibraryImplementation(def.mainPropertiesFile, def.privatePropertiesFile, type, name, description, contents)); + } + return libs; + } + + private synchronized boolean delta(Map libraries, Map newLibraries) { + if (!listening) { + return false; + } + Set added = new HashSet(newLibraries.keySet()); + added.removeAll(libraries.keySet()); + Set removed = new HashSet(); + for (Map.Entry entry : libraries.entrySet()) { + String name = entry.getKey(); + ProjectLibraryImplementation old = entry.getValue(); + ProjectLibraryImplementation nue = newLibraries.get(name); + if (nue == null) { + removed.add(name); + continue; + } + if (!old.type.equals(nue.type)) { + // Cannot fire this. + added.add(name); + removed.add(name); + libraries.put(name, nue); + continue; + } + assert old.name.equals(nue.name); + if (!Utilities.compareObjects(old.description, nue.description)) { + old.description = nue.description; + old.pcs.firePropertyChange(LibraryImplementation.PROP_DESCRIPTION, null, null); + } + if (!old.contents.equals(nue.contents)) { + old.contents = nue.contents; + old.pcs.firePropertyChange(LibraryImplementation.PROP_CONTENT, null, null); + } + } + for (String name : added) { + libraries.put(name, newLibraries.get(name)); + } + for (String name : removed) { + libraries.remove(name); + } + return !added.isEmpty() || !removed.isEmpty(); + } + + static final class ProjectLibraryImplementation implements LibraryImplementation { + + final File mainPropertiesFile, privatePropertiesFile; + final String type; + String name; + String description; + Map> contents; + final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + + ProjectLibraryImplementation(File mainPropertiesFile, File privatePropertiesFile, String type, String name, String description, Map> contents) { + this.mainPropertiesFile = mainPropertiesFile; + this.privatePropertiesFile = privatePropertiesFile; + this.type = type; + this.name = name; + this.description = description; + this.contents = contents; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getLocalizingBundle() { + return null; + } + + public List getContent(String volumeType) throws IllegalArgumentException { + List content = contents.get(volumeType); + if (content == null) { + content = Collections.emptyList(); + } + return content; + } + + public void setName(String name) { + this.name = name; + pcs.firePropertyChange(LibraryImplementation.PROP_NAME, null, null); + throw new UnsupportedOperationException(); // XXX will anyone call this? + } + + public void setDescription(String text) { + throw new UnsupportedOperationException(); // XXX will anyone call this? + } + + public void setContent(String volumeType, List path) throws IllegalArgumentException { + if (path.equals(getContent(volumeType))) { + return; + } + List value = new ArrayList(); + for (URL entry : path) { + if ("jar".equals(entry.getProtocol())) { // NOI18N + if (!entry.toExternalForm().endsWith("!/")) { // NOI18N + throw new IllegalArgumentException("Non-root folders in JARs not permitted"); // NOI18N + } + entry = FileUtil.getArchiveFile(entry); + } else if (!"file".equals(entry.getProtocol())) { // NOI18N + throw new IllegalArgumentException(entry.toExternalForm()); + } + File f = LibrariesSupport.convertURLToFile(entry); + // store properties always separated by '/' for consistency + String s; + if (f.isAbsolute()) { + s = f.getAbsolutePath().replace('\\', '/'); + } else { + s = "${base}/" + f.getPath().replace('\\', '/'); // NOI18N + } + if (value.size()+1 != path.size()) { + s += File.pathSeparatorChar; + } + value.add(s); + } + String key = "libs." + name + "." + volumeType; // NOI18N + try { + replaceProperty(mainPropertiesFile, true, key, value.toArray(new String[value.size()])); + } catch (IOException x) { + throw new IllegalArgumentException(x); + } + } + + public void setLocalizingBundle(String resourceName) { + throw new UnsupportedOperationException(); + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + pcs.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + pcs.removePropertyChangeListener(l); + } + + @Override + public String toString() { + return "ProjectLibraryImplementation[name=" + name + ",file=" + mainPropertiesFile + ",contents=" + contents + "]"; // NOI18N + } + + } + + private static void replaceProperty(File propfile, boolean classPathLikeValue, String key, String... value) throws IOException { + EditableProperties ep = new EditableProperties(); + if (propfile.isFile()) { + InputStream is = new FileInputStream(propfile); + try { + ep.load(is); + } finally { + is.close(); + } + } + if (Utilities.compareObjects(value, ep.getProperty(key))) { + return; + } + if (value.length > 0) { + if (classPathLikeValue) { + ep.setProperty(key, value); + } else { + assert value.length == 1 : Arrays.asList(value); + ep.setProperty(key, value[0]); + } + } else { + ep.remove(key); + } + FileObject fo = FileUtil.createData(propfile); + OutputStream os = fo.getOutputStream(); + try { + ep.store(os); + } finally { + os.close(); + } + } + + static final class ProjectLibraryArea implements LibraryStorageArea { + + final File mainPropertiesFile; + + ProjectLibraryArea(File mainPropertiesFile) { + assert mainPropertiesFile.getName().endsWith(".properties") : mainPropertiesFile; + this.mainPropertiesFile = mainPropertiesFile; + } + + public String getDisplayName() { + return mainPropertiesFile.getAbsolutePath(); + } + + public URL getLocation() { + try { + return mainPropertiesFile.toURI().toURL(); + } catch (MalformedURLException x) { + throw new AssertionError(x); + } + } + + public boolean equals(Object obj) { + return obj instanceof ProjectLibraryArea && ((ProjectLibraryArea) obj).mainPropertiesFile.equals(mainPropertiesFile); + } + + public int hashCode() { + return mainPropertiesFile.hashCode(); + } + + @Override + public String toString() { + return "ProjectLibraryArea[" + mainPropertiesFile + "]"; // NOI18N + } + + } + + /** + * Used from {@link AntProjectHelper#getProjectLibrariesPropertyProvider}. + * @param helper a project + * @return a provider of project library definition properties + */ + public static PropertyProvider createPropertyProvider(final AntProjectHelper helper) { + class PP implements PropertyProvider, FileChangeSupportListener, AntProjectListener { + final ChangeSupport cs = new ChangeSupport(this); + final Set listeningTo = new HashSet(); + { + helper.addAntProjectListener(WeakListeners.create(AntProjectListener.class, this, helper)); + } + private void listenTo(File f, Set noLongerListeningTo) { + if (f != null) { + noLongerListeningTo.remove(f); + if (listeningTo.add(f)) { + FileChangeSupport.DEFAULT.addListener(this, f); + } + } + } + public synchronized Map getProperties() { + Map m = new HashMap(); + // XXX add an AntProjectListener + Set noLongerListeningTo = new HashSet(listeningTo); + Definitions def = findDefinitions(helper); + if (def != null) { + m.putAll(def.properties(true)); + listenTo(def.mainPropertiesFile, noLongerListeningTo); + listenTo(def.privatePropertiesFile, noLongerListeningTo); + } + for (File f : noLongerListeningTo) { + listeningTo.remove(f); + FileChangeSupport.DEFAULT.removeListener(this, f); + } + return m; + } + public void addChangeListener(ChangeListener l) { + cs.addChangeListener(l); + } + public void removeChangeListener(ChangeListener l) { + cs.removeChangeListener(l); + } + public void fileCreated(FileChangeSupportEvent event) { + fireChangeNowOrLater(); + } + public void fileDeleted(FileChangeSupportEvent event) { + fireChangeNowOrLater(); + } + public void fileModified(FileChangeSupportEvent event) { + fireChangeNowOrLater(); + } + void fireChangeNowOrLater() { + // See PropertyUtils.FilePropertyProvider. + if (!cs.hasListeners()) { + return; + } + final Mutex.Action action = new Mutex.Action() { + public Void run() { + cs.fireChange(); + return null; + } + }; + if (ProjectManager.mutex().isWriteAccess() || FIRE_CHANGES_SYNCH) { + ProjectManager.mutex().readAccess(action); + } else if (ProjectManager.mutex().isReadAccess()) { + action.run(); + } else { + RP.post(new Runnable() { + public void run() { + ProjectManager.mutex().readAccess(action); + } + }); + } + } + public void configurationXmlChanged(AntProjectEvent ev) { + cs.fireChange(); + } + public void propertiesChanged(AntProjectEvent ev) {} + } + return new PP(); + } + private static final RequestProcessor RP = new RequestProcessor("ProjectLibraryProvider.RP"); // NOI18N + public static boolean FIRE_CHANGES_SYNCH = false; // used by tests + + /** + * Is this library reachable from this project? Returns true if given library + * is defined in libraries location associated with this project. + */ + public static boolean isReachableLibrary(Library library, AntProjectHelper helper) { + URL location = library.getManager().getLocation(); + if (location == null) { + return false; + } + ProjectLibraryArea area = INSTANCE.loadArea(location); + if (area == null) { + return false; + } + ProjectLibraryImplementation pli = INSTANCE.getLibraries(area).getLibrary(library.getName()); + if (pli == null) { + return false; + } + Definitions def = findDefinitions(helper); + if (def == null) { + return false; + } + return def.mainPropertiesFile.equals(pli.mainPropertiesFile); + } + + /** + * Create element for shared libraries to store in project.xml. + * + * @param doc XML document + * @param location project relative or absolute OS path; cannot be null + * @return element + */ + public static Element createLibrariesElement(Document doc, String location) { + Element libraries = doc.createElementNS(NAMESPACE, EL_LIBRARIES); + libraries.appendChild(libraries.getOwnerDocument().createElementNS(NAMESPACE, EL_DEFINITIONS)). + appendChild(libraries.getOwnerDocument().createTextNode(location)); + return libraries; + } + + /** + * Used from {@link ReferenceHelper#getProjectLibraryManager}. + */ + public static LibraryManager getProjectLibraryManager(AntProjectHelper helper) { + Definitions defs = findDefinitions(helper); + if (defs != null) { + try { + return LibraryManager.forLocation(defs.mainPropertiesFile.toURI().toURL()); + } catch (MalformedURLException x) { + Exceptions.printStackTrace(x); + } + } + return null; + } + + /** + * Stores given libraries location in given project. + */ + public static void setLibrariesLocation(AntProjectHelper helper, String librariesDefinition) { + Element libraries = helper.createAuxiliaryConfiguration().getConfigurationFragment(EL_LIBRARIES, NAMESPACE, true); + if (libraries == null) { + libraries = XMLUtil.createDocument("dummy", null, null, null).createElementNS(NAMESPACE, EL_LIBRARIES); // NOI18N + } else { + List elements = Util.findSubElements(libraries); + if (elements.size() == 1) { + libraries.removeChild(elements.get(0)); + } + } + libraries.appendChild(libraries.getOwnerDocument().createElementNS(NAMESPACE, EL_DEFINITIONS)). + appendChild(libraries.getOwnerDocument().createTextNode(librariesDefinition)); + helper.createAuxiliaryConfiguration().putConfigurationFragment(libraries, true); + } + + /** + * Used from {@link org.netbeans.spi.project.support.ant.SharabilityQueryImpl}. + */ + public static List getUnsharablePathsWithinProject(AntProjectHelper helper) { + List paths = new ArrayList(); + Definitions defs = findDefinitions(helper); + if (defs != null) { + if (defs.privatePropertiesFile != null) { + paths.add(defs.privatePropertiesFile.getAbsolutePath()); + } + } + return paths; + } + + public static final class SharabilityQueryImpl implements SharabilityQueryImplementation { + + /** Default constructor for lookup. */ + public SharabilityQueryImpl() {} + + public int getSharability(File file) { + if (file.getName().endsWith("-private.properties")) { // NOI18N + return SharabilityQuery.NOT_SHARABLE; + } else { + return SharabilityQuery.UNKNOWN; + } + } + + } + + /** + * Used from {@link org.netbeans.spi.project.support.ant.ReferenceHelper}. + */ + public static Library copyLibrary(final Library lib, final URL location, + final boolean generateLibraryUniqueName) throws IOException { + final File libBaseFolder = LibrariesSupport.convertURLToFile(location).getParentFile(); + FileObject sharedLibFolder; + try { + sharedLibFolder = ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction() { + public FileObject run() throws IOException { + FileObject lf = FileUtil.toFileObject(libBaseFolder); + return lf.createFolder(getUniqueName(lf, lib.getName(), null)); + } + }); + } catch (MutexException ex) { + throw (IOException)ex.getException(); + } + final Map> content = new HashMap>(); + String[] volumes = LibrariesSupport.getLibraryTypeProvider(lib.getType()).getSupportedVolumeTypes(); + for (String volume : volumes) { + List volumeContent = new ArrayList(); + for (URL libEntry : lib.getContent(volume)) { + if ("jar".equals(libEntry.getProtocol())) { // NOI18N + libEntry = FileUtil.getArchiveFile(libEntry); + } + FileObject libEntryFO = URLMapper.findFileObject(libEntry); + if (libEntryFO == null) { + if (!"file".equals(libEntry.getProtocol()) && // NOI18N + !"nbinst".equals(libEntry.getProtocol())) { // NOI18N + Logger.getLogger(ProjectLibraryProvider.class.getName()).warning("copyLibrary is ignoring entry "+libEntry); + continue; + } else { + Logger.getLogger(ProjectLibraryProvider.class.getName()).warning("Library '"+lib.getDisplayName()+ // NOI18N + "' contains entry ("+libEntry+") which does not exist. This entry is ignored and will not be copied to sharable libraries location."); // NOI18N + continue; + } + } + FileObject newFO; + String name; + if (libEntryFO.isFolder()) { + newFO = FileChooserAccessory.copyFolderRecursively(libEntryFO, sharedLibFolder); + name = sharedLibFolder.getName()+File.separatorChar+newFO.getName()+File.separatorChar; + } else { + String libEntryName = getUniqueName(sharedLibFolder, libEntryFO.getName(), libEntryFO.getExt()); + newFO = FileUtil.copyFile(libEntryFO, sharedLibFolder, libEntryName); + name = sharedLibFolder.getName()+File.separatorChar+newFO.getNameExt(); + } + volumeContent.add(LibrariesSupport.convertFileToURL(new File(name))); + } + content.put(volume, volumeContent); + } + final LibraryManager man = LibraryManager.forLocation(location); + try { + return ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction() { + public Library run() throws IOException { + String name = lib.getName(); + if (generateLibraryUniqueName) { + int index = 2; + while (man.getLibrary(name) != null) { + name = lib.getName() + "-" + index; + index++; + } + } + return man.createLibrary(lib.getType(), name, content); + } + }); + } catch (MutexException ex) { + throw (IOException)ex.getException(); + } + } + + /** + * Generate unique file name for the given folder, base name and optionally extension. + * @param baseFolder folder to generate new file name in + * @param nameFileName file name without extension + * @param extension can be null for folder + * @return new file name without extension + */ + private static String getUniqueName(FileObject baseFolder, String nameFileName, String extension) { + int suffix = 2; + String name = nameFileName; //NOI18N + while (baseFolder.getFileObject(name + (extension != null ? "." + extension : "")) != null) { + name = nameFileName + "-" + suffix; // NOI18N + suffix++; + } + return name; + } + +} Index: ant/project/src/org/netbeans/modules/project/ant/RelativizeFilePathCustomizer.form diff -u /dev/null ant/project/src/org/netbeans/modules/project/ant/RelativizeFilePathCustomizer.form:1.1.2.1 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/src/org/netbeans/modules/project/ant/RelativizeFilePathCustomizer.form Tue Jan 8 06:13:43 2008 @@ -0,0 +1,66 @@ + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Index: ant/project/src/org/netbeans/modules/project/ant/RelativizeFilePathCustomizer.java diff -u /dev/null ant/project/src/org/netbeans/modules/project/ant/RelativizeFilePathCustomizer.java:1.1.2.1 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/src/org/netbeans/modules/project/ant/RelativizeFilePathCustomizer.java Tue Jan 8 06:13:44 2008 @@ -0,0 +1,133 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * 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. + */ + +package org.netbeans.modules.project.ant; + +import java.awt.BorderLayout; +import java.io.File; +import java.io.IOException; + +/** + * Wrapper around FileChooserAccessory to let user decide how to reference file. + */ +public class RelativizeFilePathCustomizer extends javax.swing.JPanel { + + private File fileToRelativize; + private File baseFolder; + private File sharedLibrariesFolder; + private boolean copyAllowed; + private FileChooserAccessory fileChooserAccessory; + + /** Creates new form RelativizeFilePathCustomizer */ + public RelativizeFilePathCustomizer(File fileToRelativize, File baseFolder, File sharedLibrariesFolder, boolean copyAllowed) { + this.fileToRelativize = fileToRelativize; + this.baseFolder = baseFolder; + this.sharedLibrariesFolder = sharedLibrariesFolder; + this.copyAllowed = copyAllowed; + initComponents(); + fileChooserAccessory = new FileChooserAccessory(baseFolder, sharedLibrariesFolder, copyAllowed, fileToRelativize); + jPanel1.setLayout(new BorderLayout()); + jPanel1.add(fileChooserAccessory); + } + + public File getFile() throws IOException { + fileChooserAccessory.copyFilesIfNecessary(); + if (fileChooserAccessory.isRelative()) { + return fileChooserAccessory.getFiles()[0]; + } else { + return fileToRelativize; + } + } + + + /** 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. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + jLabel1 = new javax.swing.JLabel(); + jPanel1 = new javax.swing.JPanel(); + + jLabel1.setText(java.text.MessageFormat.format(org.openide.util.NbBundle.getMessage(RelativizeFilePathCustomizer.class, "RelativizeFilePathCustomizer.jLabel1.text"), new Object[] {fileToRelativize.getName()})); // NOI18N + + org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(0, 380, Short.MAX_VALUE) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(0, 258, Short.MAX_VALUE) + ); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(jPanel1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(jLabel1)) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(jLabel1) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jPanel1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + private javax.swing.JPanel jPanel1; + // End of variables declaration//GEN-END:variables + +} Index: ant/project/src/org/netbeans/modules/project/ant/ant-project-libraries-1.xsd diff -u /dev/null ant/project/src/org/netbeans/modules/project/ant/ant-project-libraries-1.xsd:1.1.2.5 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/src/org/netbeans/modules/project/ant/ant-project-libraries-1.xsd Tue Jan 15 00:49:34 2008 @@ -0,0 +1,32 @@ + + + + + + + + + + + Index: ant/project/src/org/netbeans/spi/project/support/ant/AntProjectHelper.java diff -u ant/project/src/org/netbeans/spi/project/support/ant/AntProjectHelper.java:1.45 ant/project/src/org/netbeans/spi/project/support/ant/AntProjectHelper.java:1.41.6.9 --- ant/project/src/org/netbeans/spi/project/support/ant/AntProjectHelper.java:1.45 Mon Oct 22 10:30:56 2007 +++ ant/project/src/org/netbeans/spi/project/support/ant/AntProjectHelper.java Tue Jan 15 02:03:46 2008 @@ -60,6 +60,7 @@ import org.netbeans.modules.project.ant.FileChangeSupport; import org.netbeans.modules.project.ant.FileChangeSupportEvent; import org.netbeans.modules.project.ant.FileChangeSupportListener; +import org.netbeans.modules.project.ant.ProjectLibraryProvider; import org.netbeans.modules.project.ant.UserQuestionHandler; import org.netbeans.modules.project.ant.Util; import org.netbeans.spi.project.AuxiliaryConfiguration; @@ -1019,6 +1020,11 @@ * It is permitted, and harmless, to include items that overlap others. For example, * you can have both a directory and one of its children in the include list. *

+ *

+ * Whether or not you use this method, all files named *-private.properties + * outside the project are marked unsharable, as are such files inside the project if currently referenced + * as project libraries. (See {@link #getProjectLibrariesPropertyProvider}.) + *

*
*

* Typical usage would be: @@ -1072,12 +1078,59 @@ public PropertyProvider getStockPropertyPreprovider() { return properties.getStockPropertyPreprovider(); } + + /** + * Creates a property provider which can load definitions of project libraries. + * If this project refers to any project library definition files, they will + * be included, with ${base} replaced by the appropriate value. + * @return a property provider + * @since org.netbeans.modules.project.ant/1 1.19 + * @see Schema for project library references + */ + public PropertyProvider getProjectLibrariesPropertyProvider() { + return ProjectLibraryProvider.createPropertyProvider(this); + } + + /** + * Is this project shared with other or not, that is is it using shrared + * libraries or not. + * @return true for shared project + * @since org.netbeans.modules.project.ant/1 1.19 + */ + public boolean isSharableProject() + { + return getLibrariesLocation() != null; + } + + /** + * Returns location of shared libraries associated with this project or null. + * @return relative or absolute OS path or null + * @since org.netbeans.modules.project.ant/1 1.19 + */ + public String getLibrariesLocation() + { + return ProjectLibraryProvider.getLibrariesLocationText(this.createAuxiliaryConfiguration()); + } + + /** + * Change project's associated shared libraries location. If location is + * null then project will not have shared libraries and will + * be considered as not being shared. + * + * @param location project relative or absolute OS path or null + * @since org.netbeans.modules.project.ant/1 1.18 + */ + public void setLibrariesLocation(String location) + { + ProjectLibraryProvider.setLibrariesLocation(this, location); + } /** * Get a property evaluator that can evaluate properties according to the default * file layout for Ant-based projects. * First, {@link #getStockPropertyPreprovider stock properties} are predefined. * Then {@link #PRIVATE_PROPERTIES_PATH} is loaded via {@link #getPropertyProvider}, + * then {@link #getProjectLibrariesPropertyProvider}, * then global definitions from {@link PropertyUtils#globalPropertyProvider} * (though these may be overridden using the property user.properties.file * in private.properties), then {@link #PROJECT_PROPERTIES_PATH}. @@ -1127,6 +1180,7 @@ return PropertyUtils.resolvePath(FileUtil.toFile(dir), path); } + @Override public String toString() { return "AntProjectHelper[" + getProjectDirectory() + "]"; // NOI18N } Index: ant/project/src/org/netbeans/spi/project/support/ant/ProjectGenerator.java diff -u ant/project/src/org/netbeans/spi/project/support/ant/ProjectGenerator.java:1.10 ant/project/src/org/netbeans/spi/project/support/ant/ProjectGenerator.java:1.9.10.2 --- ant/project/src/org/netbeans/spi/project/support/ant/ProjectGenerator.java:1.10 Wed Sep 26 14:15:30 2007 +++ ant/project/src/org/netbeans/spi/project/support/ant/ProjectGenerator.java Tue Dec 11 01:43:26 2007 @@ -41,12 +41,14 @@ package org.netbeans.spi.project.support.ant; +import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectManager; import org.netbeans.modules.project.ant.AntBasedProjectFactorySingleton; +import org.netbeans.modules.project.ant.ProjectLibraryProvider; import org.openide.filesystems.FileLock; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; @@ -90,10 +92,24 @@ * new project on disk is recognized by some other factory */ public static AntProjectHelper createProject(final FileObject directory, final String type) throws IOException, IllegalArgumentException { - return createProject0(directory, type, null); + return createProject0(directory, type, null, null); } - private static AntProjectHelper createProject0(final FileObject directory, final String type, final String name) throws IOException, IllegalArgumentException { + /** + * See {@link #createProject(FileObject, String)} for more datails. This + * method in addition allows to setup shared libraries location + * @param directory the main project directory to create it in + * (see {@link AntProjectHelper#getProjectDirectory}) + * @param type a unique project type identifier (see {@link AntBasedProjectType#getType}) + * @param librariesDefinition relative or absolute OS path; can be null + */ + public static AntProjectHelper createProject(final FileObject directory, final String type, + final String librariesDefinition) throws IOException, IllegalArgumentException { + return createProject0(directory, type, null, librariesDefinition); + } + + private static AntProjectHelper createProject0(final FileObject directory, final String type, + final String name, final String librariesDefinition) throws IOException, IllegalArgumentException { try { return ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction() { public AntProjectHelper run() throws IOException { @@ -116,6 +132,18 @@ } el = doc.createElementNS(AntProjectHelper.PROJECT_NS, "configuration"); // NOI18N doc.getDocumentElement().appendChild(el); + if (librariesDefinition != null) { + el.appendChild(ProjectLibraryProvider.createLibrariesElement(doc, librariesDefinition)); + // create libraries property file if it does not exist: + File f = new File(librariesDefinition); + if (!f.isAbsolute()) { + f = new File(FileUtil.toFile(directory), librariesDefinition); + } + f = FileUtil.normalizeFile(f); + if (!f.exists()) { + FileUtil.createData(f); + } + } FileLock lock = projectXml.lock(); try { OutputStream os = projectXml.getOutputStream(lock); Index: ant/project/src/org/netbeans/spi/project/support/ant/ProjectProperties.java diff -u ant/project/src/org/netbeans/spi/project/support/ant/ProjectProperties.java:1.22 ant/project/src/org/netbeans/spi/project/support/ant/ProjectProperties.java:1.19.2.3 --- ant/project/src/org/netbeans/spi/project/support/ant/ProjectProperties.java:1.22 Mon Oct 22 10:30:58 2007 +++ ant/project/src/org/netbeans/spi/project/support/ant/ProjectProperties.java Thu Nov 22 08:19:01 2007 @@ -409,6 +409,7 @@ standardPropertyEvaluator = PropertyUtils.sequentialPropertyEvaluator( getStockPropertyPreprovider(), getPropertyProvider(AntProjectHelper.PRIVATE_PROPERTIES_PATH), + helper.getProjectLibrariesPropertyProvider(), globalProperties, getPropertyProvider(AntProjectHelper.PROJECT_PROPERTIES_PATH)); } Index: ant/project/src/org/netbeans/spi/project/support/ant/ReferenceHelper.java diff -u ant/project/src/org/netbeans/spi/project/support/ant/ReferenceHelper.java:1.41 ant/project/src/org/netbeans/spi/project/support/ant/ReferenceHelper.java:1.35.8.18 --- ant/project/src/org/netbeans/spi/project/support/ant/ReferenceHelper.java:1.41 Thu Nov 15 02:57:44 2007 +++ ant/project/src/org/netbeans/spi/project/support/ant/ReferenceHelper.java Tue Jan 15 02:03:46 2008 @@ -45,6 +45,7 @@ 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; @@ -55,6 +56,7 @@ import java.util.Properties; import java.util.Set; import java.util.TreeSet; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.netbeans.api.project.Project; @@ -62,8 +64,12 @@ import org.netbeans.api.project.ProjectUtils; import org.netbeans.api.project.ant.AntArtifact; import org.netbeans.api.project.ant.AntArtifactQuery; +import org.netbeans.api.project.libraries.Library; +import org.netbeans.api.project.libraries.LibraryChooser; +import org.netbeans.api.project.libraries.LibraryManager; import org.netbeans.api.queries.CollocationQuery; import org.netbeans.modules.project.ant.AntBasedProjectFactorySingleton; +import org.netbeans.modules.project.ant.ProjectLibraryProvider; import org.netbeans.modules.project.ant.Util; import org.netbeans.spi.project.AuxiliaryConfiguration; import org.netbeans.spi.project.SubprojectProvider; @@ -72,6 +78,7 @@ import org.openide.filesystems.FileUtil; import org.openide.util.Mutex; import org.openide.util.NbCollections; +import org.openide.util.Parameters; import org.openide.xml.XMLUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -352,6 +359,25 @@ }; } assert !Arrays.asList(values).contains(null) : "values=" + Arrays.toString(values) + " base=" + base + " path=" + path; // #119847 + return setPathPropertyImpl(propertyName, values, propertiesFiles); + } + + /** + * Helper method which in project properties file sets up property with + * the given name and with (possibly relative) path value. + * @return was there any change or not + */ + private boolean setPathProperty(File path, String propertyName) { + String[] propertiesFiles = new String[] { + AntProjectHelper.PROJECT_PROPERTIES_PATH + }; + String[] values = new String[] { + path.getPath() + }; + return setPathPropertyImpl(propertyName, values, propertiesFiles); + } + + private boolean setPathPropertyImpl(String propertyName, String[] values, String[] propertiesFiles) { boolean metadataChanged = false; for (int i=0; i + * Acquires write access. + * @param file a file to refer to (need not currently exist) + * @param expectedArtifactType the required {@link AntArtifact#getType} + * @return a string which can refer to that file somehow + * + * @since org.netbeans.modules.project.ant/1 1.19 + */ + public String createForeignFileReferenceAsIs(final File file, final String expectedArtifactType) { + return createForeignFileReferenceImpl(file, expectedArtifactType, false); + } + + private String createForeignFileReferenceImpl(final File file, final String expectedArtifactType, final boolean performHeuristics) { + final File normalizedFile = FileUtil.normalizeFile(file); return ProjectManager.mutex().writeAccess(new Mutex.Action() { public String run() { - AntArtifact art = AntArtifactQuery.findArtifactFromFile(file); + AntArtifact art = AntArtifactQuery.findArtifactFromFile(normalizedFile); if (art != null && art.getType().equals(expectedArtifactType) && art.getProject() != null) { try { return createForeignFileReference(art); @@ -927,8 +980,6 @@ throw new AssertionError(iae); } } else { - String propertiesFile; - String path; File myProjDir = FileUtil.toFile(AntBasedProjectFactorySingleton.getProjectFor(h).getProjectDirectory()); String fileID = file.getName(); // if the file is folder then add to ID string also parent folder name, @@ -939,11 +990,15 @@ fileID = file.getParentFile().getName()+"-"+file.getName(); } fileID = PropertyUtils.getUsablePropertyName(fileID); - String prop = findReferenceID(fileID, "file.reference.", file.getAbsolutePath()); // NOI18N + String prop = findReferenceID(fileID, "file.reference.", normalizedFile.getAbsolutePath()); // NOI18N if (prop == null) { - prop = generateUniqueID(fileID, "file.reference.", file.getAbsolutePath()); // NOI18N + prop = generateUniqueID(fileID, "file.reference.", normalizedFile.getAbsolutePath()); // NOI18N + } + if (performHeuristics) { + setPathProperty(myProjDir, normalizedFile, "file.reference." + prop); + } else { + setPathProperty(file, "file.reference." + prop); } - setPathProperty(myProjDir, file, "file.reference." + prop); return "${file.reference." + prop + '}'; // NOI18N } } @@ -951,6 +1006,30 @@ } /** + * Create an Ant-interpretable string referring to a file on disk. Compared + * to {@link #createForeignFileReference} the file does not have to be + * normalized (ie. it can be relative path to project base folder), no + * relativization or absolutization of path is done and + * reference to file is always stored in project properties. + *

+ * Acquires write access. + * @param file a file to refer to (need not currently exist) + * @param fileId + * @param propertyPrefix the prefix of the created property + * @return a string which can refer to that file somehow + * + * @since org.netbeans.modules.project.ant/1 1.19 + */ + public String createExtraForeignFileReferenceAsIs(final File file, final String property) { + return ProjectManager.mutex().writeAccess(new Mutex.Action() { + public String run() { + setPathProperty(file, property); + return "${" + property + '}'; // NOI18N + } + }); + } + + /** * Test whether file does not lie under an extra base folder and if it does * then return string in form of "${extra.base}/remaining/path"; or null. */ @@ -1127,6 +1206,7 @@ private static final Pattern FOREIGN_FILE_REFERENCE = Pattern.compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\.([\\d&&[^.${}]]+)\\}"); // NOI18N private static final Pattern FOREIGN_FILE_REFERENCE_OLD = Pattern.compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\}"); // NOI18N private static final Pattern FOREIGN_PLAIN_FILE_REFERENCE = Pattern.compile("\\$\\{file\\.reference\\.([^${}]+)\\}"); // NOI18N + private static final Pattern LIBRARY_REFERENCE = Pattern.compile("\\$\\{libs\\.([^${}]+)\\.[^${}]+\\}"); // NOI18N /** * Try to find an AntArtifact object corresponding to a given @@ -1357,8 +1437,108 @@ h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, pub); h.putProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH, priv); } + + /** + * Create a reference to one volume of a library. + * @param library a library + * @param volumeType a legal volume type for that library + * @return substitutable Ant text suitable for inclusion in a properties file when also loading {@link AntProjectHelper#getProjectLibrariesPropertyProvider} + * @see #findLibrary + * @since org.netbeans.modules.project.ant/1 1.19 + */ + public String createLibraryReference(Library library, String volumeType) { + if (library.getManager() == LibraryManager.getDefault()) { + if (h.isSharableProject()) { + throw new IllegalArgumentException("Project ["+ // NOI18N + h.getProjectDirectory()+ + "] is sharable and cannot reference global library "+library.getName()); // NOI18N + } + } else { + if (!ProjectLibraryProvider.isReachableLibrary(library, h)) { + throw new IllegalArgumentException("Project ["+ // NOI18N + h.getProjectDirectory()+ + "] cannot reference a library from "+library.getManager().getLocation()); // NOI18N + } + } + return "${libs." + library.getName() + "." + volumeType + "}"; // NOI18N + } + + /** + * Gets a library manager corresponding to library definition file referred to from this project. + * There is no guarantee that the manager is the same object from call to call + * even if the location remain the same; in particular, it is not guaranteed that + * the manager match that returned from {@link Library#getManager} for libraries added + * from {@link #createLibraryReference}. + * @return a library manager associated with project's libraries or null if project is + * not shared (will not include {@link LibraryManager#getDefault}) + * @see #createLibraryReference + * @see #findLibrary + * @since org.netbeans.modules.project.ant/1 1.19 + */ + public LibraryManager getProjectLibraryManager() { + return ProjectLibraryProvider.getProjectLibraryManager(h); + } + + /** + * Copy global IDE library to sharable libraries definition associated with + * this project. Does nothing if project is not sharable. + * + *

Library creation is done under write access of ProjectManager.mutex(). + * + * @param lib global library; cannot be null + * @return newly created sharable version of library in case of sharable + * project or given global library in case of non-sharable project + * @throws java.io.IOException if there was problem copying files + * @throws IllegalArgumentException if library is not global one or library + * with this name already exists in sharable libraries definition + * @since org.netbeans.modules.project.ant/1 1.19 + */ + public Library copyLibrary(Library lib) throws IOException { + Parameters.notNull("lib", lib); + if (lib.getManager() != LibraryManager.getDefault()) { + throw new IllegalArgumentException("cannot copy non-global library "+lib.getManager().getLocation()); // NOI18N + } + if (!h.isSharableProject()) { + return lib; + } + File mainPropertiesFile = h.resolveFile(h.getLibrariesLocation()); + return ProjectLibraryProvider.copyLibrary(lib, mainPropertiesFile.toURI().toURL(), false); + } /** + * Returns library import handler which imports global library to sharable + * one. See {@link LibraryChooser#showDialog} for usage of this handler. + * @return copy handler + * @since org.netbeans.modules.project.ant/1 1.19 + */ + public LibraryChooser.LibraryImportHandler getLibraryChooserImportHandler() { + return new LibraryChooser.LibraryImportHandler() { + public Library importLibrary(Library library) throws IOException { + return copyLibrary(library); + } + }; + } + /** + * Tries to find a library by name in library manager associated with the project. + * It is not guaranteed that any returned library is an identical object to one which passed in to {@link #createLibraryReference}. + * @param name either a bare {@link Library#getName}, or a reference as created by {@link #createLibraryReference} + * @return the first library to be found matching that name, or null if not found + * @since org.netbeans.modules.project.ant/1 1.19 + */ + public Library findLibrary(String name) { + Matcher m = LIBRARY_REFERENCE.matcher(name); + if (m.matches()) { + name = m.group(1); + } + LibraryManager mgr = getProjectLibraryManager(); + if (mgr == null) { + return LibraryManager.getDefault().getLibrary(name); + } else { + return mgr.getLibrary(name); + } + } + + /** * A raw reference descriptor representing a link to a foreign project * and some build artifact used from it. * This class corresponds directly to what it stored in project.xml @@ -1638,7 +1818,11 @@ public String getID() { return artifactID; } - + + /** + * Get an extra properties used for target execution. + * @return a set of properties (may be empty but not null) + */ public Properties getProperties() { return props; } Index: ant/project/src/org/netbeans/spi/project/support/ant/SharabilityQueryImpl.java diff -u ant/project/src/org/netbeans/spi/project/support/ant/SharabilityQueryImpl.java:1.6 ant/project/src/org/netbeans/spi/project/support/ant/SharabilityQueryImpl.java:1.5.18.2 --- ant/project/src/org/netbeans/spi/project/support/ant/SharabilityQueryImpl.java:1.6 Wed Sep 26 14:15:32 2007 +++ ant/project/src/org/netbeans/spi/project/support/ant/SharabilityQueryImpl.java Thu Nov 22 08:19:09 2007 @@ -47,6 +47,7 @@ import java.util.ArrayList; import java.util.List; import org.netbeans.api.queries.SharabilityQuery; +import org.netbeans.modules.project.ant.ProjectLibraryProvider; import org.netbeans.spi.queries.SharabilityQueryImplementation; import org.openide.util.WeakListeners; @@ -54,7 +55,7 @@ * Standard impl of {@link SharabilityQueryImplementation}. * @author Jesse Glick */ -final class SharabilityQueryImpl implements SharabilityQueryImplementation, PropertyChangeListener { +final class SharabilityQueryImpl implements SharabilityQueryImplementation, PropertyChangeListener, AntProjectListener { private final AntProjectHelper h; private final PropertyEvaluator eval; @@ -72,12 +73,13 @@ this.excludes = excludes; computeFiles(); eval.addPropertyChangeListener(WeakListeners.propertyChange(this, eval)); + h.addAntProjectListener(this); } /** Compute the absolute paths which are and are not sharable. */ private void computeFiles() { - String[] _includePaths = computeFrom(includes); - String[] _excludePaths = computeFrom(excludes); + String[] _includePaths = computeFrom(includes, false); + String[] _excludePaths = computeFrom(excludes, true); synchronized (this) { includePaths = _includePaths; excludePaths = _excludePaths; @@ -85,7 +87,7 @@ } /** Compute a list of absolute paths based on some abstract names. */ - private String[] computeFrom(String[] list) { + private String[] computeFrom(String[] list, boolean excludeProjectLibraryPrivate) { List result = new ArrayList(list.length); for (String s : list) { String val = eval.evaluate(s); @@ -94,6 +96,9 @@ result.add(f.getAbsolutePath()); } } + if (excludeProjectLibraryPrivate) { + result.addAll(ProjectLibraryProvider.getUnsharablePathsWithinProject(h)); + } // XXX should remove overlaps somehow return result.toArray(new String[result.size()]); } @@ -132,5 +137,11 @@ public void propertyChange(PropertyChangeEvent evt) { computeFiles(); } + + public void configurationXmlChanged(AntProjectEvent ev) { + computeFiles(); + } + + public void propertiesChanged(AntProjectEvent ev) {} } Index: ant/project/test/unit/src/org/netbeans/modules/project/ant/FileOwnerCollocationQueryImplTest.java diff -u /dev/null ant/project/test/unit/src/org/netbeans/modules/project/ant/FileOwnerCollocationQueryImplTest.java:1.1.2.2 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/test/unit/src/org/netbeans/modules/project/ant/FileOwnerCollocationQueryImplTest.java Thu Dec 6 07:22:33 2007 @@ -0,0 +1,129 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 2007 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.project.ant; + +import org.netbeans.modules.project.ant.*; +import org.netbeans.modules.project.ant.FileOwnerCollocationQueryImpl; +import java.io.File; +import java.io.IOException; +import org.netbeans.api.project.TestUtil; +import org.netbeans.junit.NbTestCase; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.util.test.MockLookup; + +/** + * + * @author mkleint + */ +public class FileOwnerCollocationQueryImplTest extends NbTestCase { + + public FileOwnerCollocationQueryImplTest(String testName) { + super(testName); + } + private FileObject scratch; + + @Override + protected void setUp() throws Exception { + super.setUp(); + MockLookup.setInstances(TestUtil.testProjectFactory()); + scratch = TestUtil.makeScratchDir(this); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Test of findRoot method, of class FileOwnerCollocationQueryImpl. + */ + public void testFindRoot() throws IOException { + FileObject root = scratch.createFolder("root"); + FileObject projdir = root.createFolder("prj1"); + projdir.createFolder("testproject"); + + //root/prj1/foo + FileOwnerCollocationQueryImpl instance = new FileOwnerCollocationQueryImpl(); + assertEquals(instance.findRoot(FileUtil.toFile(projdir.createData("foo"))), FileUtil.toFile(root)); + + //root/prj2/foo/prj3/bar + projdir = root.createFolder("prj2"); + projdir.createFolder("testproject"); + projdir = projdir.createFolder("foo").createFolder("prj3"); + projdir.createFolder("testproject"); + assertEquals(instance.findRoot(FileUtil.toFile(projdir.createData("bar"))), FileUtil.toFile(root)); + + //root + assertEquals(instance.findRoot(FileUtil.toFile(root)), null); + } + + /** + * Test of areCollocated method, of class FileOwnerCollocationQueryImpl. + */ + public void testAreCollocated() throws IOException { + FileObject root = scratch.createFolder("root"); + FileObject projdir = scratch.createFolder("prj1"); + projdir.createFolder("testproject"); + FileObject lib = root.createFolder("libs"); + + + File file1 = FileUtil.toFile(lib.createData("pron")); + File file2 = FileUtil.toFile(projdir.createData("xxx")); + FileOwnerCollocationQueryImpl instance = new FileOwnerCollocationQueryImpl(); + assertTrue(instance.areCollocated(file1, file2)); + + file1 = FileUtil.toFile(projdir); + file2 = FileUtil.toFile(lib); + assertTrue(instance.areCollocated(file1, file2)); + + projdir = root.createFolder("noproj").createFolder("proj1"); + projdir.createFolder("testproject"); + FileObject projdir2 = root.createFolder("noproj2").createFolder("proj2"); + projdir2.createFolder("testproject"); + file1 = FileUtil.toFile(projdir.createData("foo")); + file2 = FileUtil.toFile(projdir2.createData("bar")); +// System.out.println("root1=" + instance.findRoot(file1)); +// System.out.println("root2=" + instance.findRoot(file2)); + assertFalse(instance.areCollocated(file1, file2)); + + } + +} Index: ant/project/test/unit/src/org/netbeans/modules/project/ant/ProjectLibraryProviderTest.java diff -u /dev/null ant/project/test/unit/src/org/netbeans/modules/project/ant/ProjectLibraryProviderTest.java:1.1.2.19 --- /dev/null Wed Jan 16 00:13:33 2008 +++ ant/project/test/unit/src/org/netbeans/modules/project/ant/ProjectLibraryProviderTest.java Mon Jan 14 02:33:50 2008 @@ -0,0 +1,468 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.modules.project.ant; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.TreeMap; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.netbeans.api.project.Project; +import org.netbeans.api.project.ProjectManager; +import org.netbeans.api.project.TestUtil; +import org.netbeans.api.project.libraries.Library; +import org.netbeans.api.project.libraries.LibraryManager; +import org.netbeans.api.project.ui.OpenProjects; +import org.netbeans.api.queries.SharabilityQuery; +import org.netbeans.junit.NbTestCase; +import org.netbeans.spi.project.AuxiliaryConfiguration; +import org.netbeans.spi.project.libraries.LibraryImplementation; +import org.netbeans.spi.project.libraries.LibraryProvider; +import org.netbeans.spi.project.libraries.LibraryTypeProvider; +import org.netbeans.spi.project.libraries.support.LibrariesSupport; +import org.netbeans.spi.project.support.ant.AntBasedTestUtil; +import org.netbeans.spi.project.support.ant.AntProjectHelper; +import org.netbeans.spi.project.support.ant.EditableProperties; +import org.netbeans.spi.project.support.ant.ProjectGenerator; +import org.netbeans.spi.project.support.ant.PropertyProvider; +import org.netbeans.spi.queries.CollocationQueryImplementation; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.filesystems.Repository; +import org.openide.loaders.DataFolder; +import org.openide.loaders.InstanceDataObject; +import org.openide.util.NbCollections; +import org.openide.util.lookup.Lookups; +import org.openide.util.test.MockChangeListener; +import org.openide.util.test.MockLookup; +import org.openide.util.test.MockPropertyChangeListener; +import org.openide.xml.XMLUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class ProjectLibraryProviderTest extends NbTestCase { + + public ProjectLibraryProviderTest(String name) { + super(name); + } + + private FileObject projdir; + private AntProjectHelper helper; + private Project project; + private URL base; + private TestLibraryProvider libraryProvider; + + + @Override + protected void setUp() throws Exception { + super.setUp(); + libraryProvider = new TestLibraryProvider(); + MockLookup.setLookup(Lookups.fixed(AntBasedTestUtil.testAntBasedProjectType(), AntBasedTestUtil.testCollocationQueryImplementation(getWorkDir()), libraryProvider), + // Filter out standard CQIs since they are bogus. + Lookups.exclude(Lookups.metaInfServices(ProjectLibraryProviderTest.class.getClassLoader()), CollocationQueryImplementation.class)); + projdir = TestUtil.makeScratchDir(this).createFolder("prj"); + helper = ProjectGenerator.createProject(projdir, "test"); + project = ProjectManager.getDefault().findProject(projdir); + close(OpenProjects.getDefault().getOpenProjects()); + base = getWorkDir().toURI().toURL(); + ProjectLibraryProvider.FIRE_CHANGES_SYNCH = true; + registerTestLibraryTypeProvider(); + } + + // XXX test name/type/description + // XXX test : vs. ; and / vs. \ (in and in *.properties) + // XXX test set name, description + + public void testLibraryLoadingBasic() throws Exception { + writeProperties("libs/my libraries.properties", + "libs.jgraph.classpath=${base}/jgraph.jar:${base}/../extra libs/jgraph-extras.jar", + "libs.jgraph.javadoc=${base}/api/jgraph-docs", + "irrelevant=stuff"); + storeDefs(project, "../libs/my libraries.properties"); + Library lib = LibraryManager.forLocation(new URL(base, "libs/my%20libraries.properties")).getLibrary("jgraph"); + assertNotNull(lib); + assertEquals("jgraph", lib.getName()); + assertEquals("jgraph", lib.getDisplayName()); + assertNull(lib.getDescription()); + assertEquals("j2se", lib.getType()); + assertEquals(Arrays.asList(new URL("jar:file:jgraph.jar!/"), new URL("jar:file:../extra%20libs/jgraph-extras.jar!/")), lib.getContent("classpath")); + assertEquals(Collections.singletonList(new URL("file:api/jgraph-docs/")), lib.getContent("javadoc")); + assertEquals(Collections.emptyList(), lib.getContent("src")); + } + + public void testLibraryLoadingPrivateAbsolute() throws Exception { + writeProperties("libs/libraries.properties", + "libs.jgraph.classpath=${base}/jgraph.jar"); + writeProperties("libs/libraries-private.properties", + "libs.jgraph.src=" + new File(getWorkDir(), "jgraph-src.zip"), + "libs.jgraph.javadoc=" + new File(getWorkDir(), "jgraph-api")); + storeDefs(project, "../libs/libraries.properties"); + Library lib = LibraryManager.forLocation(new URL(base, "libs/libraries.properties")).getLibrary("jgraph"); + assertEquals(Collections.singletonList(new URL("jar:file:jgraph.jar!/")), lib.getContent("classpath")); + assertEquals(Collections.singletonList(new URL("jar:" + base + "jgraph-src.zip!/")), lib.getContent("src")); + assertEquals(Collections.singletonList(new URL(base, "jgraph-api/")), lib.getContent("javadoc")); + } + + public void testPrivateOverridesSharedProperties() throws Exception { + writeProperties("libs/libraries.properties", + "libs.jgraph.classpath=${base}/jgraph.jar"); + writeProperties("libs/libraries-private.properties", + "libs.jgraph.classpath=" + new File(getWorkDir(), "jgraph-api")); + storeDefs(project, "../libs/libraries.properties"); + Library lib = LibraryManager.forLocation(new URL(base, "libs/libraries.properties")).getLibrary("jgraph"); + assertEquals(Collections.singletonList(new URL(base, "jgraph-api/")), lib.getContent("classpath")); + } + + public void testSetContent() throws Exception { + writeProperties("libs/libraries.properties", + "libs.jgraph.classpath="); + storeDefs(project, "../libs/libraries.properties"); + Library lib = LibraryManager.forLocation(new URL(base, "libs/libraries.properties")).getLibrary("jgraph"); + setLibraryContent(lib, "classpath", new URL("jar:file:jgraph.jar!/"), new URL("jar:file:../extra%20libs/jgraph-extras.jar!/")); + setLibraryContent(lib, "src", new URL(base, "separate/jgraph-src/"), new URL(base, "jgraph-other-src/")); + setLibraryContent(lib, "javadoc", new URL("jar:" + base + "separate/jgraph-api.zip!/")); + Map m = new HashMap(); + File separate = new File(getWorkDir(), "separate"); + m.put("libs.jgraph.classpath", "${base}/jgraph.jar"+File.pathSeparatorChar+"${base}/../extra libs/jgraph-extras.jar"); + m.put("libs.jgraph.src", new File(separate, "jgraph-src").getAbsolutePath().replace('\\', '/') + File.pathSeparator + + new File(getWorkDir(), "jgraph-other-src").getAbsolutePath().replace('\\', '/')); + m.put("libs.jgraph.javadoc", new File(separate, "jgraph-api.zip").getAbsolutePath().replace('\\', '/')); + assertEquals(m, loadProperties("libs/libraries.properties")); + } + + public void testAreaChangesFromProjectsOpenedClosed() throws Exception { + storeDefs(project, "../libraries.properties"); + assertEquals("[]", openedLibraryManagers()); + open(project); + assertEquals("[, " + base + "libraries.properties]", openedLibraryManagers()); + close(project); + assertEquals("[]", openedLibraryManagers()); + Project project2 = ProjectManager.getDefault().findProject( + ProjectGenerator.createProject(projdir.getParent().createFolder("prj2"), "test").getProjectDirectory()); + storeDefs(project2, "../lib2.properties"); + open(project, project2); + assertEquals("[, " + base + "lib2.properties, "+ base + "libraries.properties]", openedLibraryManagers()); + close(project); + assertEquals("[, " + base + "lib2.properties]", openedLibraryManagers()); + } + private static String openedLibraryManagers() { + List urls = new ArrayList(); + for (LibraryManager mgr : LibraryManager.getOpenManagers()) { + URL u = mgr.getLocation(); + urls.add(u != null ? u.toExternalForm() : ""); + } + Collections.sort(urls); + return urls.toString(); + } + + public void testChangesLibraries() throws Exception { + PropertyProvider pp = helper.getProjectLibrariesPropertyProvider(); + writeProperties("libraries.properties", + "libs.jgraph.classpath="); + storeDefs(project, "../libraries.properties"); + LibraryManager mgr = LibraryManager.forLocation(new URL(base, "libraries.properties")); + Library lib1 = mgr.getLibrary("jgraph"); + assertEquals(Collections.emptyList(), lib1.getContent("classpath")); + assertEquals("{libs.jgraph.classpath=}", new TreeMap(pp.getProperties()).toString()); + MockPropertyChangeListener liblist = new MockPropertyChangeListener(LibraryManager.PROP_LIBRARIES); + MockPropertyChangeListener contentlist = new MockPropertyChangeListener(Library.PROP_CONTENT); + mgr.addPropertyChangeListener(liblist); + lib1.addPropertyChangeListener(contentlist); + MockChangeListener pplist = new MockChangeListener(); + pp.addChangeListener(pplist); + writeProperties("libraries.properties", + "libs.jgraph.classpath=${base}/jgraph", + "libs.collections.classpath=${base}/collections"); + contentlist.assertEventCount(1); + assertEquals(Collections.singletonList(new URL("file:jgraph/")), lib1.getContent("classpath")); + liblist.assertEventCount(1); + assertEquals(lib1, mgr.getLibrary("jgraph")); + Library lib2 = mgr.getLibrary("collections"); + assertEquals(Collections.singletonList(new URL("file:collections/")), lib2.getContent("classpath")); + pplist.assertEventCount(1); + assertEquals(("{libs.collections.classpath=" + getWorkDir() + "/collections, libs.jgraph.classpath=" + + getWorkDir() + "/jgraph}").replace('/', File.separatorChar), + new TreeMap(pp.getProperties()).toString()); + writeProperties("others.properties", + "libs.jrcs.classpath="); + storeDefs(project, "../others.properties"); + contentlist.assertEventCount(0); + liblist.assertEventCount(0); + pplist.assertEventCount(1); + assertEquals(("{libs.jrcs.classpath=}").replace('/', File.separatorChar), + new TreeMap(pp.getProperties()).toString()); + } + + public void testCreateRemoveLibrary() throws Exception { + LibraryManager mgr = LibraryManager.forLocation(new URL(base, "libraries.properties")); + Map> content = new HashMap>(); + content.put("classpath", Arrays.asList(new URL("jar:file:jh.jar!/"), new URL("jar:file:jh-search.jar!/"))); + content.put("javadoc", Arrays.asList(new URL("file:jh-api/"))); + Library lib = mgr.createLibrary("j2se", "javahelp", content); + assertEquals("j2se", lib.getType()); + assertEquals("javahelp", lib.getName()); + assertEquals(content.get("classpath"), lib.getContent("classpath")); + assertEquals(content.get("javadoc"), lib.getContent("javadoc")); + lib = mgr.createLibrary("j2me", "gps", Collections.>emptyMap()); + assertEquals("j2me", lib.getType()); + assertEquals("gps", lib.getName()); + Map expected = new HashMap(); + expected.put("libs.javahelp.classpath", "${base}/jh.jar"+File.pathSeparatorChar+"${base}/jh-search.jar"); + expected.put("libs.javahelp.javadoc", "${base}/jh-api"); + expected.put("libs.gps.type", "j2me"); + assertEquals(expected, loadProperties("libraries.properties")); + mgr.removeLibrary(lib); + expected.remove("libs.gps.type"); + assertEquals(expected, loadProperties("libraries.properties")); + } + + public void testPropertyProviderBasic() throws Exception { + writeProperties("libs/libraries.properties", + "libs.jgraph.classpath=${base}/jgraph.jar:${base}/../extralibs/jgraph-extras.jar"); + storeDefs(project, "../libs/libraries.properties"); + PropertyProvider pp = helper.getProjectLibrariesPropertyProvider(); + assertEquals(Collections.singletonMap("libs.jgraph.classpath", (getWorkDir() + "/libs/jgraph.jar:" + + getWorkDir() + "/libs/../extralibs/jgraph-extras.jar").replace('/', File.separatorChar)), pp.getProperties()); + } + + public void testPropertyProviderPrivateAbsolute() throws Exception { + writeProperties("libs/libraries.properties", + "libs.jgraph.classpath=${base}/jgraph-1.0.jar"); + writeProperties("libs/libraries-private.properties", + "libs.jgraph.classpath=" + new File(getWorkDir(), "jgraph-2.0-beta.jar")); + storeDefs(project, "../libs/libraries.properties"); + PropertyProvider pp = helper.getProjectLibrariesPropertyProvider(); + assertEquals(Collections.singletonMap("libs.jgraph.classpath", new File(getWorkDir(), "jgraph-2.0-beta.jar").getAbsolutePath()), pp.getProperties()); + } + + public void testSharability() throws Exception { + assertSharability(SharabilityQuery.UNKNOWN, "libs/index.properties"); + assertSharability(SharabilityQuery.NOT_SHARABLE, "libs/index-private.properties"); + assertSharability(SharabilityQuery.SHARABLE, "prj/libs/index.properties"); + assertSharability(SharabilityQuery.NOT_SHARABLE, "prj/libs/index-private.properties"); + assertSharability(SharabilityQuery.SHARABLE, "prj/libs/"); + storeDefs(project, "libs/index.properties"); + assertSharability(SharabilityQuery.SHARABLE, "prj/libs/index.properties"); + assertSharability(SharabilityQuery.NOT_SHARABLE, "prj/libs/index-private.properties"); + assertSharability(SharabilityQuery.MIXED, "prj/libs/"); + } + private void assertSharability(int mode, String path) throws Exception { + File f = new File(getWorkDir(), path.replace('/', File.separatorChar)); + if (path.endsWith("/")) { + FileUtil.createFolder(f); + } else { + FileUtil.createData(f); + } + assertEquals(mode, SharabilityQuery.getSharability(f)); + } + + private void writeProperties(String path, String... properties) throws IOException { + FileObject f = FileUtil.createData(FileUtil.toFileObject(getWorkDir()), path); + EditableProperties ep = new EditableProperties(); + for (String def : properties) { + String[] nameValue = def.split("=", 2); + ep.put(nameValue[0], nameValue[1]); + } + OutputStream os = f.getOutputStream(); + ep.store(os); + os.close(); + } + + private static void storeDefs(Project project, String... definitions) throws IOException { + Document doc = XMLUtil.createDocument("x", null, null, null); + Element libraries = doc.createElementNS("http://www.netbeans.org/ns/ant-project-libraries/1", "libraries"); + for (String def : definitions) { + libraries.appendChild(doc.createElementNS("http://www.netbeans.org/ns/ant-project-libraries/1", "definitions")).appendChild(doc.createTextNode(def)); + } + project.getLookup().lookup(AuxiliaryConfiguration.class).putConfigurationFragment(libraries, true); + ProjectManager.getDefault().saveProject(project); // to assist in debugging + } + + private static void open(Project... projects) { + OpenProjects.getDefault().open(projects, false); + } + + private static void close(Project... projects) { + OpenProjects.getDefault().close(projects); + } + + private static void setLibraryContent(Library lib, String volumeType, URL... paths) throws Exception { + MockPropertyChangeListener l = new MockPropertyChangeListener(Library.PROP_CONTENT); + lib.addPropertyChangeListener(l); + LibraryImplementation impl = getLibraryImplementation(lib); + List path = Arrays.asList(paths); + impl.setContent(volumeType, path); + l.assertEventCount(1); + assertEquals(path, lib.getContent(volumeType)); + } + + private static LibraryImplementation getLibraryImplementation(Library lib) throws Exception { + Method getLibraryImplementation = Library.class.getDeclaredMethod("getLibraryImplementation"); + getLibraryImplementation.setAccessible(true); + return (LibraryImplementation) getLibraryImplementation.invoke(lib); + } + + private Map loadProperties(String path) throws IOException { + File f = new File(getWorkDir(), path.replace('/', File.separatorChar)); + if (!f.isFile()) { + return Collections.emptyMap(); + } + Properties p = new Properties(); + InputStream is = new FileInputStream(f); + p.load(is); + is.close(); + return NbCollections.checkedMapByFilter(p, String.class, String.class, true); + } + + /** + * Test of copyLibrary method, of class LibrariesSupport. + */ + public void testCopyLibrary() throws Exception { + File f = new File(this.getWorkDir(), "bertie.jar"); + createFakeJAR(f); + File f1 = new File(this.getWorkDir(), "dog.jar"); + createFakeJAR(f1); + new File(this.getWorkDir(), "sources").mkdir(); + File f2 = new File(this.getWorkDir(), "sources/bertie.jar"); + createFakeJAR(f2); + new File(this.getWorkDir(), "libraries").mkdir(); + File f3 = new File(this.getWorkDir(), "libraries/libs.properties"); + f3.createNewFile(); + LibraryImplementation l1 = LibrariesSupport.createLibraryImplementation("j2test", new String[]{"jars", "sources"}); + l1.setName("vino"); + l1.setContent("jars", Arrays.asList(new URL[]{f.toURI().toURL(), f1.toURI().toURL()})); + l1.setContent("sources", Arrays.asList(new URL[]{f2.toURI().toURL()})); + libraryProvider.set(l1); + Library l = LibraryManager.getDefault().getLibrary("vino"); + assertNotNull(l); + assertEquals(LibraryManager.getDefault(), l.getManager()); + URL u = f3.toURI().toURL(); + Library result = ProjectLibraryProvider.copyLibrary(l, u, false); + assertNotNull(result); + assertEquals(u, result.getManager().getLocation()); + assertEquals(Arrays.asList(new URL("jar:file:vino/bertie.jar!/"), + new URL("jar:file:vino/dog.jar!/")), result.getContent("jars")); + assertEquals(Arrays.asList(new URL("jar:file:vino/bertie-2.jar!/")), result.getContent("sources")); + assertEquals("vino", result.getName()); + assertEquals("j2test", result.getType()); + assertEquals(new File(this.getWorkDir(), "libraries/vino/bertie.jar").getPath(), + FileUtil.toFile(LibrariesSupport.resolveLibraryEntryFileObject(u, FileUtil.getArchiveFile(result.getContent("jars").get(0)))).getPath()); + } + + private void createFakeJAR(File f) throws IOException { + // create just enough to make URLMapper recognize file as JAR: + ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(f)); + zos.putNextEntry(new ZipEntry("test")); + zos.finish(); + zos.close(); + } + + public static class TestLibraryProvider implements LibraryProvider { + + public final List libs = new ArrayList(); + final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + + public void set(LibraryImplementation... nue) { + libs.clear(); + libs.addAll(Arrays.asList(nue)); + pcs.firePropertyChange(PROP_LIBRARIES, null, null); + } + + public LibraryImplementation[] getLibraries() { + return libs.toArray(new LibraryImplementation[0]); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + } + + public static class TestLibraryTypeProvider implements LibraryTypeProvider { + + + public String getDisplayName() { + return "j2test"; + } + + public String getLibraryType() { + return "j2test"; + } + + public String[] getSupportedVolumeTypes() { + return new String[] {"jars","sources"}; + } + + public LibraryImplementation createLibrary() { + return LibrariesSupport.createLibraryImplementation("j2test", new String[] {"jars","sources"}); + } + + public void libraryDeleted(LibraryImplementation library) { + } + + public void libraryCreated(LibraryImplementation library) { + } + + public java.beans.Customizer getCustomizer(String volumeType) { + return null; + } + + public org.openide.util.Lookup getLookup() { + return null; + } + } + + private static void registerTestLibraryTypeProvider () throws Exception { + StringTokenizer tk = new StringTokenizer("org-netbeans-api-project-libraries/LibraryTypeProviders","/"); + FileObject root = Repository.getDefault().getDefaultFileSystem().getRoot(); + while (tk.hasMoreElements()) { + String pathElement = tk.nextToken(); + FileObject tmp = root.getFileObject(pathElement); + if (tmp == null) { + tmp = root.createFolder(pathElement); + } + root = tmp; + } + if (root.getChildren().length == 0) { + InstanceDataObject.create (DataFolder.findFolder(root),"TestLibraryTypeProvider",TestLibraryTypeProvider.class); + } + } +} Index: ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBasedTestUtil.java diff -u ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBasedTestUtil.java:1.25 ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBasedTestUtil.java:1.21.2.7 --- ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBasedTestUtil.java:1.25 Wed Sep 26 14:15:37 2007 +++ ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBasedTestUtil.java Thu Nov 22 08:19:22 2007 @@ -104,7 +104,7 @@ * with namespaces urn:test:shared and urn:test:private. * Loading the project succeeds unless there is a file in it nbproject/broken. * The project's methods mostly delegate to the helper; its lookup uses the helper's - * supports for ExtensibleMetadataProvider, ActionProvider, and SubprojectProvider, + * supports for AuxiliaryConfiguration, CacheDirectoryProvider, and SubprojectProvider, * and also adds an instance of String, namely "hello". * It also puts the AntProjectHelper into its lookup to assist in testing. * build-impl.xml is generated from data/build-impl.xsl @@ -188,6 +188,7 @@ genFilesHelper, aux, helper.createCacheDirectoryProvider(), + helper.createSharabilityQuery(helper.getStandardPropertyEvaluator(), new String[0], new String[0]), refHelper.createSubprojectProvider(), new TestAntArtifactProvider(), new ProjectXmlSavedHook() { @@ -436,7 +437,7 @@ /** * Get a sample file collocation query provider. * Files under the supplied root are normally considered to be collocated. - * However the subdirectory separate (if it exists) forms its own root. + * However the subdirectory separate (if it exists) forms its own root. * And the subdirectory transient (if it exists) does not form a root, * but any files in there are not considered collocated with anything. */ @@ -481,6 +482,11 @@ return null; } return root; + } + + @Override + public String toString() { + return "TestCollocationQueryImplementation[" + root + "]"; } } Index: ant/project/test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java diff -u ant/project/test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java:1.27 ant/project/test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java:1.25.8.9 --- ant/project/test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java:1.27 Thu Nov 15 02:57:45 2007 +++ ant/project/test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java Fri Dec 14 06:55:14 2007 @@ -44,8 +44,10 @@ import java.io.File; import java.io.OutputStream; import java.net.URI; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Properties; @@ -57,8 +59,11 @@ import org.netbeans.api.project.TestUtil; import org.netbeans.api.project.ant.AntArtifact; import org.netbeans.api.project.ant.AntArtifactQuery; +import org.netbeans.api.project.libraries.Library; +import org.netbeans.api.project.libraries.LibraryManager; import org.netbeans.api.queries.CollocationQuery; import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.project.ant.ProjectLibraryProvider; import org.netbeans.modules.project.ant.Util; import org.netbeans.modules.queries.AlwaysRelativeCollocationQuery; import org.netbeans.spi.project.AuxiliaryConfiguration; @@ -66,9 +71,8 @@ import org.openide.filesystems.FileLock; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; -import org.openide.util.Lookup; import org.openide.util.lookup.Lookups; -import org.openide.util.lookup.ProxyLookup; +import org.openide.util.test.MockLookup; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; @@ -148,17 +152,12 @@ } protected void setUp() throws Exception { - super.setUp(); - Object[] instances = new Object[] { - AntBasedTestUtil.testAntBasedProjectType(), - AntBasedTestUtil.testCollocationQueryImplementation(getWorkDir()), - }; + super.setUp(); ClassLoader l = ReferenceHelper.class.getClassLoader(); - TestUtil.setLookup (new ProxyLookup (new Lookup[] { - Lookups.fixed(instances), + MockLookup.setLookup( + Lookups.fixed(AntBasedTestUtil.testAntBasedProjectType(), AntBasedTestUtil.testCollocationQueryImplementation(getWorkDir())), Lookups.singleton(l), - Lookups.exclude(Lookups.metaInfServices(l), new Class[] {AlwaysRelativeCollocationQuery.class}) - })); + Lookups.exclude(Lookups.metaInfServices(l), AlwaysRelativeCollocationQuery.class)); scratch = TestUtil.makeScratchDir(this); projdir = scratch.createFolder("proj"); TestUtil.createFileFromContent(ReferenceHelperTest.class.getResource("data/project.xml"), projdir, "nbproject/project.xml"); @@ -219,7 +218,6 @@ p = null; h = null; //l = null; - TestUtil.setLookup(Lookup.EMPTY); super.tearDown(); } @@ -1093,6 +1091,68 @@ actualScriptLocations.add(Util.findText(script)); } assertEquals(Arrays.asList(scriptLocations), actualScriptLocations); + } + + public void testProjectLibraryReferences() throws Exception { + ProjectLibraryProvider.FIRE_CHANGES_SYNCH = true; + assertProjectLibraryManagers(null); + File fooJar = new File(getWorkDir(), "foo.jar"); + File f = new File(getWorkDir(), "libs.properties"); + URL loc = f.toURI().toURL(); + LibraryManager mgr = LibraryManager.forLocation(loc); + assertEquals(loc, mgr.getLocation()); + Library fooLib = mgr.createLibrary("j2se", "foo", + Collections.singletonMap("classpath", Arrays.asList(new URL("jar:" + fooJar.toURI() + "!/")))); + assertEquals(mgr, fooLib.getManager()); + try { + r.createLibraryReference(fooLib, "classpath"); + fail("cannot reference library which is not reachable from project"); + } catch (IllegalArgumentException ex) { + // as expected + } + h.setLibrariesLocation(f.getAbsolutePath()); + String fooref = r.createLibraryReference(fooLib, "classpath"); + assertEquals("${libs.foo.classpath}", fooref); + assertEquals(fooJar.getAbsolutePath(), pev.evaluate(fooref).replace('/', File.separatorChar)); + assertProjectLibraryManagers(loc); + assertEquals("foo", r.findLibrary("foo").getName()); + assertEquals("foo", r.findLibrary("${libs.foo.classpath}").getName()); + assertNull(r.findLibrary("nonexistent")); + assertNull(r.findLibrary("${libs.nonexistent.classpath}")); + assertNull(r.findLibrary("${some.other.foo.stuff}")); + File barDir = new File(getWorkDir(), "bar"); + barDir.mkdirs(); + String barref = r.createLibraryReference(mgr.createLibrary("j2se", "bar", + Collections.singletonMap("classpath", Arrays.asList(barDir.toURI().toURL()))), "classpath"); + assertEquals("${libs.bar.classpath}", barref); + assertEquals(barDir.getAbsolutePath(), pev.evaluate(barref).replace('/', File.separatorChar)); + EditableProperties props = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); + props.put("javac.classpath", "stuff:" + fooref + ":which-is-not-bar"); + h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props); + assertProjectLibraryManagers(loc); + assertEquals("foo", r.findLibrary("foo").getName()); + assertEquals("foo", r.findLibrary("${libs.foo.classpath}").getName()); + assertEquals("bar", r.findLibrary("bar").getName()); + assertEquals("bar", r.findLibrary("${libs.bar.classpath}").getName()); + mgr.createLibrary("j2se", "empty", Collections.>emptyMap()); + assertEquals("stuff:" + fooJar + ":which-is-not-bar", pev.evaluate("${javac.classpath}").replace('/', File.separatorChar)); + props.put("javac.classpath", "nolibshere"); + h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props); + assertEquals("nolibshere", pev.getProperty("javac.classpath")); + assertEquals(fooJar.getAbsolutePath(), pev.getProperty("libs.foo.classpath").replace('/', File.separatorChar)); + assertProjectLibraryManagers(loc); + assertEquals("foo", r.findLibrary("foo").getName()); + assertEquals("foo", r.findLibrary("${libs.foo.classpath}").getName()); + assertEquals("bar", r.findLibrary("bar").getName()); + assertEquals("bar", r.findLibrary("${libs.bar.classpath}").getName()); + } + private void assertProjectLibraryManagers(URL expectedUrl) { + LibraryManager mgr = r.getProjectLibraryManager(); + if (mgr == null) { + assertNull(expectedUrl); + } else { + assertEquals(expectedUrl, mgr.getLocation()); + } } }