package org.autoplot.pds; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; 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.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.das2.util.FileUtil; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Document and XML utilities. * @author jbf */ public class DocumentUtil { /** * dump the document to an XML file * @param doc * @param f * @throws IllegalArgumentException */ public static void dumpToXML( Document doc, File f ) throws IllegalArgumentException { Transformer transformer; try { transformer = TransformerFactory.newInstance().newTransformer(); } catch (TransformerConfigurationException ex) { throw new RuntimeException(ex); } transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); // initialize StreamResult with File object to save to file StreamResult result = new StreamResult(new StringWriter()); DOMSource source = new DOMSource(doc); try { transformer.transform(source, result); } catch (TransformerException ex) { throw new RuntimeException(ex); } String xmlString = result.getWriter().toString(); try { FileUtil.writeStringToFile(f, xmlString); } catch (IOException ex) { throw new RuntimeException(ex); } } /** * return true if the node has no children other than the text node. * @param node the node * @return */ public static boolean isLeaf(Node node) { return node.getChildNodes().getLength() == 1 && node.getFirstChild().getNodeType() == Node.TEXT_NODE; } /** * Create map from the document so that it can be used as metadata. Note * this looks for the DESCRIPTION (or description) node and will remove * extra whitespace which was used to format assuming a fixed-width font. * @param root the document. * @return a map representing the document. */ public static Map convertDocumentToMap(Node root) { Map resultMap = new HashMap<>(); NodeList nodeList = root.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); String key = node.getNodeName(); if (isLeaf(node)) { String value = node.getTextContent(); // or another method to extract the value if (key.equalsIgnoreCase("DESCRIPTION")) { value = cleanDescriptionString(value); } resultMap.put(key, value); } else if (node.getNodeType() == Node.ELEMENT_NODE) { Map subNode = convertDocumentToMap(node); resultMap.put(key, subNode); } } return resultMap; } /** * remove whitespace intended to format nicely with fixed-with fonts and replace with <br>. * @param desc * @return */ public static String cleanDescriptionString(String desc) { if (desc == null) { return null; } //desc= String.join(" ",desc.split("[\\s|\\&\\#13\\;]+")); desc = String.join(" ", desc.trim().split("\\s+")); desc = String.join("
", desc.split("\\&\\#13\\;")); return "" + desc; } /** * Misguided attempt to create a title assuming the first sentence * is a summary. For example: *
RTN normal component of the magnetic field component in nT. The normal component (N) completes the right handed ...
* becomes *
RTN normal component of the magnetic field component in nT.
* In this day of ChatGPT, this seems quite silly, but what else can be done? * @param desc a longer description. * @return a title */ public static String createTitleFrom(String desc) { if (desc == null) { return null; } desc= cleanDescriptionString(desc); int i= desc.indexOf(". "); if ( i>10 ) { desc= desc.substring(0,i); } if ( desc.startsWith("") ) { desc= desc.substring(6); } return desc; } /** * Read the XML file into a document. * @param f the file * @return the document object * @throws IOException * @throws SAXException */ public static Document readXML(File f) throws IOException, SAXException { DocumentBuilder builder = null; try { builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); } catch (ParserConfigurationException ex) { throw new RuntimeException(ex); } Document document; try (InputStream in = new FileInputStream(f)) { InputSource source = new InputSource(in); document = builder.parse(source); } return document; } }