Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nb/o.n.upgrader/nbproject/project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
106 changes: 104 additions & 2 deletions nb/o.n.upgrader/src/org/netbeans/upgrade/CopyFiles.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -54,6 +73,7 @@ final class CopyFiles {
private Set<String> excludePatterns = new HashSet<>();
private HashMap<String, String> translatePatterns = new HashMap<>(); // <originalPath, newPath>
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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Comment thread
NicolaIsotta marked this conversation as resolved.
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<Node> 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();
Comment thread
NicolaIsotta marked this conversation as resolved.
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> 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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE attributes PUBLIC \"-//NetBeans//DTD DefaultAttributes 1.0//EN\" \"http://www.netbeans.org/dtds/attributes-1_0.dtd\">\n"
+ "<attributes version=\"1.0\">\n"
+ " <fileobject name=\"Templates\">\n"
+ " <attr name=\"AuxilaryConfiguration\" stringvalue=\"someValue\"/>\n"
+ " </fileobject>\n"
+ " <fileobject name=\"Cache\">\n"
+ " <attr name=\"CacheTimestamp\" stringvalue=\"12345678\"/>\n"
+ " </fileobject>\n"
+ "</attributes>\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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,8 @@
<attr name="displayName" bundlevalue="org.netbeans.core.ui.options.general.Bundle#General.Options.Export.displayName"/>
</file>
<file name="Other">
<attr name="include" stringvalue="config/Preferences/.*"/>
<!-- var/attributes.xml is filtered in org.netbeans.upgrade.CopyFiles -->
<attr name="include" stringvalue="config/Preferences/.*|var/attributes.xml"/>
Comment thread
NicolaIsotta marked this conversation as resolved.
<attr name="displayName" bundlevalue="org.netbeans.core.ui.options.general.Bundle#Other.Options.Export.displayName"/>
</file>
</folder>
Expand Down
Loading