Bug 2766: Fixed parsing and serializing XPath Instance Identifiers
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / codec / xml / InstanceIdentifierForXmlCodec.java
index 6051b2681388aeb09fbf3213cba99d5ec33c259d..ce304c82eb74dc878f50ecd45a200df25fd216d2 100644 (file)
  */
 package org.opendaylight.yangtools.yang.data.impl.codec.xml;
 
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-
 import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
 import java.util.Map.Entry;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.w3c.dom.Element;
 
 public final class InstanceIdentifierForXmlCodec {
-    private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
-    private static final Splitter SLASH_SPLITTER = Splitter.on('/');
-    private static final Splitter COLON_SPLITTER = Splitter.on(':');
-
     private InstanceIdentifierForXmlCodec() {
         throw new UnsupportedOperationException("Utility class");
     }
 
-    public static InstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) {
-        Preconditions.checkNotNull(element, "Value of element for deserialization can't be null");
-        Preconditions.checkNotNull(schemaContext,
-                "Schema context for deserialization of instance identifier type can't be null");
-
-        final String valueTrimmed = element.getTextContent().trim();
-        final Iterator<String> xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator();
-
-        // must be at least "/pr:node"
-        if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
-            return null;
-        }
-
-        List<PathArgument> result = new ArrayList<>();
-        while (xPathParts.hasNext()) {
-            String xPathPartTrimmed = xPathParts.next().trim();
-
-            PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext);
-            if (pathArgument != null) {
-                result.add(pathArgument);
-            }
-        }
-        return InstanceIdentifier.create(result);
+    public static YangInstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) {
+        final ElementInstanceIdentifierParser codec = new ElementInstanceIdentifierParser(schemaContext, element);
+        return codec.deserialize(element.getTextContent().trim());
     }
 
-    public static Element serialize(final InstanceIdentifier id, final Element element) {
-        Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
-        Preconditions.checkNotNull(element, "DOM element can't be null");
-
-        final RandomPrefix prefixes = new RandomPrefix();
-        final String str = XmlUtils.encodeIdentifier(prefixes, id);
+    public static Element serialize(final YangInstanceIdentifier id, final Element element, SchemaContext context) {
+        final RandomPrefixInstanceIdentifierSerializer codec = new RandomPrefixInstanceIdentifierSerializer(context);
+        final String str = codec.serialize(id);
 
-        for (Entry<URI, String> e: prefixes.getPrefixes()) {
+        for (Entry<URI, String> e : codec.getPrefixes()) {
             element.setAttribute("xmlns:" + e.getValue(), e.getKey().toString());
         }
         element.setTextContent(str);
         return element;
     }
 
+    /**
+     *
+     * @deprecated USe {@link #serialize(YangInstanceIdentifier, Element, SchemaContext)} instead.
+     */
+    @Deprecated
+    public static Element serialize(final YangInstanceIdentifier id, final Element element) {
+        throw new UnsupportedOperationException("Not supported, due to buggy API contract.");
+    }
+
     private static String getIdAndPrefixAsStr(final String pathPart) {
         int predicateStartIndex = pathPart.indexOf('[');
         return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
     }
 
-    private static PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
-        final QName mainQName = toIdentity(xPathArgument, element, schemaContext);
-
-        // predicates
-        final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
-        final Map<QName, Object> predicates = new HashMap<>();
-        QName currentQName = mainQName;
-
-        while (matcher.find()) {
-            final String predicateStr = matcher.group(1).trim();
-            final int indexOfEqualityMark = predicateStr.indexOf('=');
-            if (indexOfEqualityMark != -1) {
-                final String predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
-                if (predicateValue == null) {
-                    return null;
-                }
-
-                if (predicateStr.charAt(0) != '.') {
-                    // target is not a leaf-list
-                    currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext);
-                    if (currentQName == null) {
-                        return null;
-                    }
-                }
-                predicates.put(currentQName, predicateValue);
-            }
-        }
-
-        if (predicates.isEmpty()) {
-            return new InstanceIdentifier.NodeIdentifier(mainQName);
-        } else {
-            return new InstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates);
-        }
-
-    }
-
     public static QName toIdentity(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
-        final String xPathPartTrimmed = getIdAndPrefixAsStr(xPathArgument).trim();
-        final Iterator<String> it = COLON_SPLITTER.split(xPathPartTrimmed).iterator();
-
-        // Empty string
-        if (!it.hasNext()) {
-            return null;
-        }
-
-        final String prefix = it.next().trim();
-        if (prefix.isEmpty()) {
-            return null;
-        }
-
-        // it is not "prefix:value"
-        if (!it.hasNext()) {
-            return null;
-        }
-
-        final String identifier = it.next().trim();
-        if (identifier.isEmpty()) {
-            return null;
-        }
-
-        URI namespace = null;
-        String namespaceStr = null;
-        try {
-            namespaceStr = element.lookupNamespaceURI(prefix);
-            namespace = new URI(namespaceStr);
-        } catch (URISyntaxException e) {
-            throw new IllegalArgumentException("It wasn't possible to convert " + namespaceStr + " to URI object.");
-        } catch (NullPointerException e) {
-            throw new IllegalArgumentException("I wasn't possible to get namespace for prefix " + prefix);
-        }
-
-        Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
-        return QName.create(module.getQNameModule(), identifier);
-    }
-
-    private static String trimIfEndIs(final String str, final char end) {
-        final int l = str.length() - 1;
-        if (str.charAt(l) != end) {
-            return null;
-        }
-
-        return str.substring(1, l);
+        final ElementIdentityrefParser codec = new ElementIdentityrefParser(schemaContext, element);
+        return codec.deserialize(getIdAndPrefixAsStr(xPathArgument).trim());
     }
 
-    private static String toPredicateValue(final String predicatedValue) {
-        final String predicatedValueTrimmed = predicatedValue.trim();
-        if (predicatedValue.isEmpty()) {
-            return null;
-        }
-
-        switch (predicatedValueTrimmed.charAt(0)) {
-        case '"':
-            return trimIfEndIs(predicatedValueTrimmed, '"');
-        case '\'':
-            return trimIfEndIs(predicatedValueTrimmed, '\'');
-        default:
-            return null;
-        }
-    }
 }