diff --git a/nb/o.n.upgrader/nbproject/project.properties b/nb/o.n.upgrader/nbproject/project.properties index 58e6bb108e8e..4830bfd4015f 100644 --- a/nb/o.n.upgrader/nbproject/project.properties +++ b/nb/o.n.upgrader/nbproject/project.properties @@ -16,7 +16,7 @@ # under the License. javac.compilerargs=-Xlint:unchecked -javac.release=11 +javac.release=21 module.jar.dir=core javadoc.arch=${basedir}/arch.xml diff --git a/nb/o.n.upgrader/src/org/netbeans/upgrade/CopyFiles.java b/nb/o.n.upgrader/src/org/netbeans/upgrade/CopyFiles.java index 64fd9abc90e7..b36bb6d1c58a 100644 --- a/nb/o.n.upgrader/src/org/netbeans/upgrade/CopyFiles.java +++ b/nb/o.n.upgrader/src/org/netbeans/upgrade/CopyFiles.java @@ -28,16 +28,35 @@ import java.io.OutputStream; import java.io.Reader; import java.nio.file.Files; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; + import javax.swing.JDialog; import javax.swing.JOptionPane; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.XMLConstants; + import org.netbeans.util.Util; import org.openide.util.EditableProperties; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; import static java.nio.charset.StandardCharsets.UTF_8; @@ -54,6 +73,7 @@ final class CopyFiles { private Set excludePatterns = new HashSet<>(); private HashMap translatePatterns = new HashMap<>(); // private static final Logger LOGGER = Logger.getLogger(CopyFiles.class.getName()); + private static final String ATTRIBUTES_XML_PATH = "var/attributes.xml"; //NOI18N private CopyFiles(File source, File target, File patternsFile) { this.sourceRoot = source; @@ -179,8 +199,13 @@ && new String(Files.readAllBytes(sourceFile.toPath()), UTF_8).indexOf('\u0000') File targetFile = new File(targetRoot, relativePath); LOGGER.log(Level.FINE, "Path: {0}", relativePath); //NOI18N if (includeKeys.isEmpty() && excludeKeys.isEmpty()) { - // copy entire file - copyFile(sourceFile, targetFile); + if (ATTRIBUTES_XML_PATH.equals(relativePath)) { + // filter var/attributes.xml to keep only AuxilaryConfiguration + copyFilteredAttributesXml(sourceFile, targetFile); + } else { + // copy entire file + copyFile(sourceFile, targetFile); + } } else { if (!includeKeys.isEmpty()) { currentProperties.keySet().retainAll(includeKeys); @@ -232,6 +257,83 @@ private EditableProperties getProperties(String relativePath) throws IOException return properties; } + /** + * Copies var/attributes.xml, filtering to keep only fileobject entries that + * contain an Auxil[i]aryConfiguration attribute. + * + * @param sourceFile source attributes.xml + * @param targetFile target attributes.xml + * @throws IOException if parsing or writing fails + */ + private static void copyFilteredAttributesXml(File sourceFile, File targetFile) throws IOException { + try { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); //NOI18N + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(sourceFile); + Element root = doc.getDocumentElement(); + NodeList children = root.getChildNodes(); + + // Collect fileobject nodes to remove (can't modify NodeList while iterating) + List toRemove = new ArrayList<>(); + for (int i = 0; i < children.getLength(); i++) { + if (children.item(i) instanceof Element el + && "fileobject".equals(el.getTagName()) //NOI18N + && !hasAuxiliaryConfiguration(el)) { + toRemove.add(el); + } + } + + for (Node node : toRemove) { + root.removeChild(node); + } + + // Only write if there are remaining entries + if (root.getElementsByTagName("fileobject").getLength() == 0) { //NOI18N + LOGGER.log(Level.FINE, "No Auxil[i]aryConfiguration entries found in {0}, skipping", sourceFile); //NOI18N + return; + } + + TransformerFactory tf = TransformerFactory.newInstance(); + tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + Transformer transformer = tf.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //NOI18N + transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, + "-//NetBeans//DTD DefaultAttributes 1.0//EN"); //NOI18N + transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, + "http://www.netbeans.org/dtds/attributes-1_0.dtd"); //NOI18N + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //NOI18N + ensureParent(targetFile); + try (OutputStream out = new FileOutputStream(targetFile)) { + transformer.transform(new DOMSource(doc), new StreamResult(out)); + } + LOGGER.log(Level.FINE, "Filtered attributes.xml copied to {0}", targetFile); //NOI18N + } catch (ParserConfigurationException | SAXException | TransformerException ex) { + throw new IOException("Failed to filter attributes.xml: " + ex.getMessage(), ex); //NOI18N + } + } + + /** + * Checks whether a fileobject element contains an Auxil[i]aryConfiguration + * attribute. + * + * @param fileObject the fileobject element to check + * @return true if the element has a Auxil[i]aryConfiguration attribute + */ + private static boolean hasAuxiliaryConfiguration(Element fileObject) { + NodeList attrs = fileObject.getElementsByTagName("attr"); //NOI18N + for (int i = 0; i < attrs.getLength(); i++) { + Element attr = (Element) attrs.item(i); + String name = attr.getAttribute("name"); + if (name != null && (name.contains("AuxilaryConfiguration") //NOI18N + || name.contains("AuxiliaryConfiguration"))) { //NOI18N + return true; + } + } + return false; + } + /** Creates parent of given file, if doesn't exist. */ private static void ensureParent(File file) throws IOException { final File parent = file.getParentFile(); diff --git a/nb/o.n.upgrader/test/unit/src/org/netbeans/upgrade/CopyFilesTest.java b/nb/o.n.upgrader/test/unit/src/org/netbeans/upgrade/CopyFilesTest.java index bda3d6117fe1..b474efb13d98 100644 --- a/nb/o.n.upgrader/test/unit/src/org/netbeans/upgrade/CopyFilesTest.java +++ b/nb/o.n.upgrader/test/unit/src/org/netbeans/upgrade/CopyFilesTest.java @@ -75,6 +75,41 @@ private static void writeTo(FileSystem fs, String res, String content) throws ja lock.releaseLock (); } + @Test + public void testCopyFilteredAttributesXml() throws Exception { + List fileList = List.of("source/var/attributes.xml", "source/foo/etc/patterns.import"); + FileSystem fs = createLocalFileSystem(fileList.toArray(String[]::new)); + + FileObject path = fs.findResource("source"); + assertNotNull(path); + FileObject tg = fs.getRoot().createFolder("target"); + assertNotNull(tg); + FileObject patterns = FileUtil.createData(fs.getRoot(), "source/foo/etc/patterns.import"); + assertNotNull(patterns); + String pattern = "include var/attributes\\.xml\n"; + writeTo(fs, "source/foo/etc/patterns.import", pattern); + + String attrXml = "\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n"; + writeTo(fs, "source/var/attributes.xml", attrXml); + + org.netbeans.upgrade.CopyFiles.copyDeep(FileUtil.toFile(path), FileUtil.toFile(tg), FileUtil.toFile(patterns)); + + FileObject targetAttrFo = tg.getFileObject("var/attributes.xml"); + assertNotNull("attributes.xml not copied", targetAttrFo); + String targetContent = targetAttrFo.asText("UTF-8"); + assertTrue("Templates attribute should be kept", targetContent.contains("Templates")); + assertFalse("CacheTimestamp attribute should be filtered out", targetContent.contains("CacheTimestamp")); + } + public LocalFileSystem createLocalFileSystem(String[] resources) throws Exception { File mountPoint = new File(getWorkDir(), "tmpfs"); mountPoint.mkdir(); diff --git a/platform/core.ui/src/org/netbeans/core/ui/resources/layer.xml b/platform/core.ui/src/org/netbeans/core/ui/resources/layer.xml index 4910d36c28e0..535864ed4721 100644 --- a/platform/core.ui/src/org/netbeans/core/ui/resources/layer.xml +++ b/platform/core.ui/src/org/netbeans/core/ui/resources/layer.xml @@ -780,7 +780,8 @@ - + +