001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029package edu.wisc.ssec.mcidasv.util; 030 031import java.util.ArrayList; 032import java.util.List; 033 034import org.w3c.dom.Attr; 035import org.w3c.dom.Element; 036import org.w3c.dom.NamedNodeMap; 037import org.w3c.dom.Node; 038import org.w3c.dom.NodeList; 039 040/** 041 * A collection of utilities for XML. 042 */ 043public abstract class XmlUtil extends ucar.unidata.xml.XmlUtil { 044 045 /** 046 * Print all the attributes of the given node 047 * 048 * @param parent Node whose attributes will be printed. 049 */ 050 public static void printNode(Node parent) { 051 if (parent == null) { 052 System.out.println("null node!"); 053 return; 054 } 055 System.out.println(parent.getNodeName() + " node:"); 056 NamedNodeMap attrs = parent.getAttributes(); 057 for(int i = 0 ; i < attrs.getLength() ; i++) { 058 Attr attribute = (Attr)attrs.item(i); 059 System.out.println(" " + attribute.getName()+" = "+attribute.getValue()); 060 } 061 } 062 063 /** 064 * Find all of the descendant elements of the given parent Node whose tag 065 * name equals the given tag. 066 * 067 * @param parent Root of the XML DOM tree to search. 068 * @param tag Tag name to match. 069 * @param separator String that separates tags into components. 070 * 071 * @return List of descendants that match the given tag. 072 */ 073 public static List<String> findDescendantNamesWithSeparator(Node parent, String tag, String separator) { 074 List<String> found = new ArrayList<>(); 075 findDescendantNamesWithSeparator(parent, tag, "", separator, found); 076 return found; 077 } 078 079 /** 080 * Find all of the descendant elements of the given parent Node whose 081 * tag name equals the given tag. 082 * 083 * @param parent Root of the XML DOM tree to search. 084 * @param tag Tag name to match. 085 * @param descendants Descendant elements. 086 * @param separator String separating tag components (also in descendants). 087 * @param found List of descendants that match the given tag. 088 */ 089 private static void findDescendantNamesWithSeparator(Node parent, String tag, String descendants, String separator, List<String> found) { 090 if (parent instanceof Element) { 091 String elementName = ((Element)parent).getAttribute("name"); 092 if (!elementName.isEmpty()) { 093 descendants += ((Element)parent).getAttribute("name"); 094 } 095 if (parent.getNodeName().equals(tag)) { 096 found.add(descendants); 097 } 098 if (!elementName.isEmpty()) { 099 descendants += separator; 100 } 101 } 102 NodeList children = parent.getChildNodes(); 103 for (int i = 0; i < children.getLength(); i++) { 104 Node child = children.item(i); 105 findDescendantNamesWithSeparator(child, tag, descendants, separator, found); 106 } 107 } 108 109 /** 110 * Find the element described by nameList (path). 111 * 112 * @param parent Node at which the search should begin. 113 * @param nameList List of node names to search for (think xpath). 114 * 115 * @return {@code Element} described by the given path, or {@code null} if 116 * there was a problem. 117 */ 118 public static Element getElementAtNamedPath(Node parent, List<String> nameList) { 119 return getMakeElementAtNamedPath(parent, nameList, "", false); 120 } 121 122 /** 123 * Make the element described by nameList (path). 124 * 125 * @param parent Node at which the search should begin. 126 * @param nameList List of node names to search for (think xpath). 127 * @param tagName Tag name to locate. 128 * 129 * @return {@code Element} described by the given path, or {@code null} if 130 * there was a problem. 131 */ 132 public static Element makeElementAtNamedPath(Node parent, List<String> nameList, String tagName) { 133 return getMakeElementAtNamedPath(parent, nameList, tagName, true); 134 } 135 136 /** 137 * Find the element described by nameList (path). 138 * 139 * @param parent Node at which the search should begin. 140 * @param nameList List of node names to search for (think xpath). 141 * @param tagName Tag name to locate. 142 * @param makeNew Whether or not a new {@code Element} should be created. 143 * 144 * @return {@code Element} described by the given path, or {@code null} if 145 * there was a problem. 146 */ 147 public static Element getMakeElementAtNamedPath(Node parent, List<String> nameList, String tagName, boolean makeNew) { 148 Element thisElement = null; 149 if ((parent instanceof Element) && !nameList.isEmpty()) { 150 for (int i=0; i < nameList.size(); i++) { 151 String thisName = nameList.get(i); 152 NodeList children = parent.getChildNodes(); 153 boolean foundChild = false; 154 for (int j = 0; j < children.getLength(); j++) { 155 Node child = children.item(j); 156 if (!(child instanceof Element)) { 157 continue; 158 } 159 if (XmlUtil.getAttribute(child, "name").equals(thisName)) { 160 if (i == (nameList.size() - 1)) { 161 thisElement = (Element)child; 162 } 163 parent = child; 164 foundChild = true; 165 break; 166 } 167 } 168 169 // Didn't find it where we expected to. Create a new one. 170 if (makeNew && !foundChild && (parent instanceof Element)) { 171 try { 172 Element newElement = XmlUtil.create(tagName, (Element)parent); 173 newElement.setAttribute("name", thisName); 174 parent.appendChild(newElement); 175 parent = newElement; 176 thisElement = newElement; 177 } catch (Exception ex) { 178 System.err.println("Error making new " + tagName + " node named " + thisName); 179 break; 180 } 181 } 182 } 183 } 184 return thisElement; 185 } 186 187 /** 188 * This method ensures that the output String has only valid XML unicode 189 * characters as specified by the XML 1.0 standard. 190 * 191 * <p>For reference, please see 192 * <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the 193 * standard</a>. This method will return an empty 194 * String if the input is null or empty. 195 * 196 * @param in String whose non-valid characters we want to remove. 197 * 198 * @return The in String, stripped of non-valid characters. 199 */ 200 // Added by TJJ Feb 2014 201 public static String stripNonValidXMLCharacters(String in) { 202 if ((in == null) || in.isEmpty()) { 203 return ""; // vacancy test. 204 } 205 StringBuilder out = new StringBuilder(in.length()); // Used to hold the output. 206 for (int i = 0; i < in.length(); i++) { 207 char current = in.charAt(i); // Used to reference the current character. 208 // NOTE: No IndexOutOfBoundsException caught here; it should not happen. 209 if ((current == 0x9) || 210 (current == 0xA) || 211 (current == 0xD) || 212 ((current >= 0x20) && (current <= 0xD7FF)) || 213 ((current >= 0xE000) && (current <= 0xFFFD)) || 214 ((current >= 0x10000) && (current <= 0x10FFFF))) 215 { 216 out.append(current); 217 } 218 } 219 return out.toString(); 220 } 221 222}