Bug 1441: Bug fixes, clean-up and test migration
[yangtools.git] / yang / yang-data-codec-xml / src / main / java / org / opendaylight / yangtools / yang / data / codec / xml / XmlCodecFactory.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.yangtools.yang.data.codec.xml;
10
11 import com.google.common.annotations.Beta;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Verify;
14 import com.google.common.cache.CacheBuilder;
15 import com.google.common.cache.CacheLoader;
16 import com.google.common.cache.LoadingCache;
17 import java.util.AbstractMap.SimpleImmutableEntry;
18 import java.util.Map.Entry;
19 import javax.annotation.concurrent.ThreadSafe;
20 import javax.xml.namespace.NamespaceContext;
21 import javax.xml.stream.XMLStreamException;
22 import javax.xml.stream.XMLStreamWriter;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
24 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
25 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
29 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
30 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
31 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
32 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
33 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
34 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 @Beta
39 @ThreadSafe
40 public final class XmlCodecFactory {
41
42     private static final Logger LOG = LoggerFactory.getLogger(XmlCodecFactory.class);
43     private static final XmlCodec<Object> NULL_CODEC = new XmlCodec<Object>() {
44         @Override
45         public Object deserialize(final String input) {
46             return null;
47         }
48
49         @Override
50         public String serialize(final Object input) {
51             return null;
52         }
53
54         @Override
55         public void serializeToWriter(final XMLStreamWriter writer, final Object value) throws XMLStreamException {
56             // NOOP since codec is unkwown.
57             LOG.warn("Call of the serializeToWriter method on XmlCodecFactory.NULL_CODEC object. No operation " +
58                     "performed.");
59         }
60     };
61
62     private final LoadingCache<Entry<DataSchemaNode, NamespaceContext>, XmlCodec<?>> codecs =
63             CacheBuilder.newBuilder().softValues().build(
64                     new CacheLoader<Entry<DataSchemaNode, NamespaceContext>, XmlCodec<?>>() {
65                 @Override
66                 public XmlCodec<?> load(final Entry<DataSchemaNode, NamespaceContext> schemaNodeAndNamespaceCtxPair)
67                         throws Exception {
68                     final DataSchemaNode schemaNode = schemaNodeAndNamespaceCtxPair.getKey();
69                     final TypeDefinition<?> type;
70                     if (schemaNode instanceof LeafSchemaNode) {
71                         type = ((LeafSchemaNode) schemaNode).getType();
72                     } else if (schemaNode instanceof LeafListSchemaNode) {
73                         type = ((LeafListSchemaNode) schemaNode).getType();
74                     } else {
75                         throw new IllegalArgumentException("Not supported node type " + schemaNode.getClass().getName());
76                     }
77                     return createCodec(schemaNode,type, schemaNodeAndNamespaceCtxPair.getValue());
78                 }
79             });
80
81     private final SchemaContext schemaContext;
82
83     private XmlCodecFactory(final SchemaContext context) {
84         this.schemaContext = Preconditions.checkNotNull(context);
85     }
86
87     /**
88      * Instantiate a new codec factory attached to a particular context.
89      *
90      * @param context SchemaContext to which the factory should be bound
91      * @return A codec factory instance.
92      */
93     public static XmlCodecFactory create(final SchemaContext context) {
94         return new XmlCodecFactory(context);
95     }
96
97     private XmlCodec<?> createCodec(final DataSchemaNode key, final TypeDefinition<?> type,
98                                     final NamespaceContext namespaceContext) {
99         if (type instanceof LeafrefTypeDefinition) {
100             return createReferencedTypeCodec(key, (LeafrefTypeDefinition) type, namespaceContext);
101         } else if (type instanceof IdentityrefTypeDefinition) {
102             final XmlCodec<?> xmlStringIdentityrefCodec =
103                     new XmlStringIdentityrefCodec(schemaContext, key.getQName().getModule(), namespaceContext);
104             return xmlStringIdentityrefCodec;
105         }
106         return createFromSimpleType(type, namespaceContext);
107     }
108
109     private XmlCodec<?> createReferencedTypeCodec(final DataSchemaNode schema, final LeafrefTypeDefinition type,
110                                                   final NamespaceContext namespaceContext) {
111         // FIXME: Verify if this does indeed support leafref of leafref
112         final TypeDefinition<?> referencedType =
113                 SchemaContextUtil.getBaseTypeForLeafRef(type, getSchemaContext(), schema);
114         Verify.verifyNotNull(referencedType, "Unable to find base type for leafref node '%s'.", schema.getPath());
115         return createCodec(schema, referencedType, namespaceContext);
116     }
117
118     private XmlCodec<?> createFromSimpleType(final TypeDefinition<?> type, final NamespaceContext namespaceContext) {
119         if (type instanceof InstanceIdentifierTypeDefinition) {
120             final XmlCodec<YangInstanceIdentifier> iidCodec = new XmlStringInstanceIdentifierCodec(schemaContext, this,
121                     namespaceContext);
122             return iidCodec;
123         }
124         if (type instanceof EmptyTypeDefinition) {
125             return XmlEmptyCodec.INSTANCE;
126         }
127
128         final TypeDefinitionAwareCodec<Object, ?> codec = TypeDefinitionAwareCodec.from(type);
129         if (codec == null) {
130             LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName().getLocalName());
131             return NULL_CODEC;
132         }
133         return AbstractXmlCodec.create(codec);
134     }
135
136     SchemaContext getSchemaContext() {
137         return schemaContext;
138     }
139
140     XmlCodec<?> codecFor(final DataSchemaNode schema, final NamespaceContext namespaceContext) {
141         return codecs.getUnchecked(new SimpleImmutableEntry<>(schema, namespaceContext));
142     }
143 }