/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.constellation.util; import org.apache.sis.util.logging.Logging; import org.geotoolkit.temporal.object.TemporalUtilities; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** * * @author Guilhem Legal (Geomatys) */ public class NodeUtilities { private static final String NULL_VALUE = "null"; private static final Logger LOGGER = Logging.getLogger("org.constellation.util"); public static List<Node> getNodes(final String propertyName, final List<Node> nodes, final int ordinal, final boolean create) { final List<Node> result = new ArrayList<>(); for (Node e : nodes) { final List<Node> nl = getChilds(e, propertyName); // add new node if (nl.isEmpty() && create) { final Element newNode = e.getOwnerDocument().createElementNS("TODO", propertyName); e.appendChild(newNode); result.add(newNode); // Select the node to update } else { for (int i = 0 ; i < nl.size(); i++) { if (ordinal == -1) { result.add(nl.get(i)); } else if (i == ordinal) { result.add(nl.get(i)); } } } } return result; } public static List<Node> getChilds(final Node n, final String propertyName) { final List<Node> results = new ArrayList<>(); if (propertyName.startsWith("@")) { final Node att = n.getAttributes().getNamedItem(propertyName.substring(1)); if (att != null) { results.add(att); } } else { final NodeList nl = n.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { final Node child = nl.item(i); if (propertyName.equals("*") || propertyName.equals(child.getLocalName())) { results.add(child); } } } return results; } public static List<Node> getNodeFromConditionalPath(final String xpath, final String conditionalPath, final String conditionalValue, final Node metadata) { final List<Node> results = new ArrayList<>(); final String[] xpart = xpath.split("/"); final String[] cpart = conditionalPath.split("/"); final int min = Math.min(xpart.length, cpart.length); int i = 0; String commonPath = ""; while (xpart[i].equals(cpart[i]) && i < min) { commonPath += "/" + xpart[i]; i++; } commonPath = commonPath.substring(1); final List<Node> nodes = getNodeFromPath(metadata, commonPath); for (Node n : nodes) { final List<Node> conditionalNode = getNodeFromPath(n, conditionalPath.substring(commonPath.length())); boolean match = false; for (Node cNode : conditionalNode) { if (conditionalValue.equalsIgnoreCase(cNode.getTextContent())) { match = true; } } if (match) { final List<Node> matchingNodes = getNodeFromPath(n, xpath.substring(commonPath.length())); results.addAll(matchingNodes); } } return results; } public static List<Node> getNodeFromPath(final Node parent, String xpath) { //we remove the type name from the xpath xpath = xpath.substring(xpath.indexOf('/') + 1); List<Node> nodes = Arrays.asList(parent); while (!xpath.isEmpty()) { //Then we get the next Property name int separator = xpath.indexOf('/'); String propertyName; if (separator != -1) { propertyName = xpath.substring(0, separator); } else { propertyName = xpath; } final int ordinal = extractOrdinal(propertyName); final int braceIndex = propertyName.indexOf('['); if (braceIndex != -1) { propertyName = propertyName.substring(0, braceIndex); } //remove namespace on propertyName final int separatorIndex = propertyName.indexOf(':'); if (separatorIndex != -1) { propertyName = propertyName.substring(separatorIndex + 1); } nodes = getNodes(propertyName, nodes, ordinal, false); if (nodes.isEmpty()) { return nodes; } separator = xpath.indexOf('/'); if (separator != -1) { xpath = xpath.substring(separator + 1); } else { xpath = ""; } } return nodes; } public static List<String> getValuesFromPath(final Node parent, final String xpath) { return getValuesFromPaths(parent, Arrays.asList(xpath)); } public static List<String> getValuesFromPaths(final Node parent, final List<String> xpaths) { final List<String> results = new ArrayList<>(); for (String xpath : xpaths) { // verify type xpath = xpath.substring(xpath.indexOf(':') + 1); final String pathType = xpath.substring(0, xpath.indexOf('/')); if (!pathType.equals("*") && !pathType.equals(parent.getLocalName())) { continue; } final List<Node> nodes = getNodeFromPath(parent, xpath); for (Node n : nodes) { results.add(n.getTextContent()); } } return results; } public static void appendChilds(final Node parent, final List<Node> children) { for (Node child : children) { parent.appendChild(child); } } /** * Return an ordinal if there is one in the propertyName specified else return -1. * example : name[1] return 1 * name return -1 * @param propertyName A property name extract from an Xpath * @return an ordinal if there is one, -1 else. */ public static int extractOrdinal(final String propertyName) { int ordinal = -1; //we extract the ordinal if there is one if (propertyName.indexOf('[') != -1) { if (propertyName.indexOf(']') != -1) { try { final String ordinalValue = propertyName.substring(propertyName.indexOf('[') + 1, propertyName.indexOf(']')); ordinal = Integer.parseInt(ordinalValue) - 1; } catch (NumberFormatException ex) { throw new IllegalArgumentException("The xpath is malformed, the brackets value is not an integer"); } } else { throw new IllegalArgumentException("The xpath is malformed, unclosed bracket"); } } return ordinal; } public static List<Node> buildNodes(final Document doc, final String namespace, final String localName, final List<String> values, final boolean mandatory) { final List<Node> nodes = new ArrayList<>(); if (mandatory && values.isEmpty()) { final Node n = doc.createElementNS(namespace, localName); nodes.add(n); } for (String value : values) { final Node n = doc.createElementNS(namespace, localName); n.setTextContent(value); nodes.add(n); } return nodes; } /** * Extract the String values denoted by the specified paths * and return the values as a String values1,values2,.... * if there is no values corresponding to the paths the method return "null" (the string) * * @param metadata * @param paths * @return */ public static List<Object> extractValues(final Node metadata, final List<String> paths) { final List<Object> response = new ArrayList<>(); if (paths != null) { for (String fullPathID: paths) { // remove Standard final String pathPrefix = fullPathID.substring(1, fullPathID.indexOf(':')); fullPathID = fullPathID.substring(fullPathID.indexOf(':') + 1); final String pathType = fullPathID.substring(0, fullPathID.indexOf('/')); if (!matchType(metadata, pathType, pathPrefix)) { continue; } String pathID; String conditionalPath = null; String conditionalValue = null; // if the path ID contains a # we have a conditional value next to the searched value. final int separator = fullPathID.indexOf('#'); if (separator != -1) { pathID = fullPathID.substring(0, separator); conditionalPath = pathID + '/' + fullPathID.substring(separator + 1, fullPathID.indexOf('=')); conditionalValue = fullPathID.substring(fullPathID.indexOf('=') + 1); int nextSeparator = conditionalValue.indexOf('/'); if (nextSeparator == -1) { throw new IllegalArgumentException("A conditionnal path must be in the form ...start_path#conditional_path=value/endPath"); } else { pathID = pathID + conditionalValue.substring(nextSeparator); conditionalValue = conditionalValue.substring(0, nextSeparator); } } else { pathID = fullPathID; } int ordinal = -1; if (pathID.endsWith("]") && pathID.indexOf('[') != -1) { try { ordinal = Integer.parseInt(pathID.substring(pathID.lastIndexOf('[') + 1, pathID.length() - 1)); } catch (NumberFormatException ex) { LOGGER.warning("Unable to parse last path ordinal"); } } final List<Node> nodes; if (conditionalPath == null) { nodes = getNodeFromPath(metadata, pathID); } else { nodes = getNodeFromConditionalPath(pathID, conditionalPath, conditionalValue, metadata); } final List<Object> value = getStringValue(nodes, ordinal); if (!value.isEmpty() && !value.equals(Arrays.asList(NULL_VALUE))) { response.addAll(value); } } } if (response.isEmpty()) { response.add(NULL_VALUE); } return response; } /** * Return a String value from the specified Object. * Let the number object as Number * * @param obj * @return */ private static List<Object> getStringValue(final List<Node> nodes, final int ordinal) { final List<Object> result = new ArrayList<>(); if (nodes != null && !nodes.isEmpty()) { for (Node n : nodes) { final String s = n.getTextContent(); final String typeName = n.getLocalName(); if (typeName == null) { result.add(s); } else if (typeName.equals("Real") || typeName.equals("Decimal")) { try { result.add(Double.parseDouble(s)); } catch (NumberFormatException ex) { LOGGER.log(Level.WARNING, "Unable to parse the real value:{0}", s); } } else if (typeName.equals("Integer")) { try { result.add(Integer.parseInt(s)); } catch (NumberFormatException ex) { LOGGER.log(Level.WARNING, "Unable to parse the integer value:{0}", s); } } else if (typeName.equals("Date") || typeName.equals("DateTime") || typeName.equals("position") || typeName.equals("beginPosition") || typeName.equals("endPosition")) { try { final Date d = TemporalUtilities.getDateFromString(s); synchronized (Util.LUCENE_DATE_FORMAT) { result.add(Util.LUCENE_DATE_FORMAT.format(d)); } } catch (ParseException ex) { LOGGER.log(Level.WARNING, "Unable to parse the date value:{0}", s); } } else if (typeName.endsWith("Corner")) { if (ordinal != -1) { final String[] parts = s.split(" "); if (ordinal < parts.length) { result.add(parts[ordinal]); } } else { result.add(s); } } else if (s != null) { result.add(s); } } } if (result.isEmpty()) { result.add(NULL_VALUE); } /*if (obj instanceof Position) { final Position pos = (Position) obj; final Date d = pos.getDate(); if (d != null) { synchronized(LUCENE_DATE_FORMAT) { result.add(LUCENE_DATE_FORMAT.format(d)); } } else { result.add(NULL_VALUE); } } else if (obj instanceof Instant) { final Instant inst = (Instant)obj; if (inst.getPosition() != null && inst.getPosition().getDate() != null) { synchronized(LUCENE_DATE_FORMAT) { result.add( LUCENE_DATE_FORMAT.format(inst.getPosition().getDate())); } } else { result.add(NULL_VALUE); } } else if (obj instanceof Date) { synchronized (LUCENE_DATE_FORMAT){ result.add(LUCENE_DATE_FORMAT.format((Date)obj)); } } else { throw new IllegalArgumentException("this type is unexpected: " + obj.getClass().getSimpleName()); }*/ return result; } private static boolean matchType(final Node n, final String type, final String prefix) { final String namespace = XpathUtils.getNamespaceFromPrefix(prefix); return (type.equals(n.getLocalName()) || type.equals("*")) && namespace.equals(n.getNamespaceURI()); } }