Use TypedSchemaNode
[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.Nonnull;
20 import javax.annotation.concurrent.ThreadSafe;
21 import javax.xml.namespace.NamespaceContext;
22 import javax.xml.stream.XMLStreamException;
23 import javax.xml.stream.XMLStreamWriter;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
26 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
27 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
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.TypedSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
32 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
33 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
34 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
35 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
36 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 @Beta
41 @ThreadSafe
42 public final class XmlCodecFactory {
43
44     private static final Logger LOG = LoggerFactory.getLogger(XmlCodecFactory.class);
45     private static final XmlCodec<Object> NULL_CODEC = new XmlCodec<Object>() {
46         @Override
47         public Object deserialize(final String input) {
48             return null;
49         }
50
51         @Override
52         public String serialize(final Object input) {
53             return null;
54         }
55
56         @Override
57         public void serializeToWriter(final XMLStreamWriter writer, final Object value) throws XMLStreamException {
58             // NOOP since codec is unkwown.
59             LOG.warn("Call of the serializeToWriter method on XmlCodecFactory.NULL_CODEC object. No operation " +
60                     "performed.");
61         }
62     };
63
64     private final LoadingCache<Entry<TypedSchemaNode, NamespaceContext>, XmlCodec<?>> codecs = CacheBuilder.newBuilder()
65             .softValues().build(new CacheLoader<Entry<TypedSchemaNode, NamespaceContext>, XmlCodec<?>>() {
66                 @Override
67                 public XmlCodec<?> load(@Nonnull final Entry<TypedSchemaNode, NamespaceContext> pair) {
68                     final TypedSchemaNode schemaNode = pair.getKey();
69                     final TypeDefinition<?> type = schemaNode.getType();
70                     return createCodec(schemaNode, type, pair.getValue());
71                 }
72             });
73
74     private final SchemaContext schemaContext;
75
76     private XmlCodecFactory(final SchemaContext context) {
77         this.schemaContext = Preconditions.checkNotNull(context);
78     }
79
80     /**
81      * Instantiate a new codec factory attached to a particular context.
82      *
83      * @param context SchemaContext to which the factory should be bound
84      * @return A codec factory instance.
85      */
86     public static XmlCodecFactory create(final SchemaContext context) {
87         return new XmlCodecFactory(context);
88     }
89
90     private XmlCodec<?> createCodec(final DataSchemaNode key, final TypeDefinition<?> type,
91                                     final NamespaceContext namespaceContext) {
92         if (type instanceof LeafrefTypeDefinition) {
93             return createReferencedTypeCodec(key, (LeafrefTypeDefinition) type, namespaceContext);
94         } else if (type instanceof IdentityrefTypeDefinition) {
95             return createIdentityrefTypeCodec(key, namespaceContext);
96         } else if (type instanceof UnionTypeDefinition) {
97             return createUnionTypeCodec(key, (UnionTypeDefinition)type, namespaceContext);
98         }
99         return createFromSimpleType(key, type, namespaceContext);
100     }
101
102     private XmlCodec<?> createReferencedTypeCodec(final DataSchemaNode schema, final LeafrefTypeDefinition type,
103                                                   final NamespaceContext namespaceContext) {
104         // FIXME: Verify if this does indeed support leafref of leafref
105         final TypeDefinition<?> referencedType =
106                 SchemaContextUtil.getBaseTypeForLeafRef(type, getSchemaContext(), schema);
107         Verify.verifyNotNull(referencedType, "Unable to find base type for leafref node '%s'.", schema.getPath());
108         return createCodec(schema, referencedType, namespaceContext);
109     }
110
111     public XmlCodec<QName> createIdentityrefTypeCodec(final DataSchemaNode schema,
112                                                       final NamespaceContext namespaceContext) {
113         final XmlCodec<QName> xmlStringIdentityrefCodec =
114                 new XmlStringIdentityrefCodec(getSchemaContext(), schema.getQName().getModule(), namespaceContext);
115         return xmlStringIdentityrefCodec;
116     }
117
118     private XmlCodec<Object> createUnionTypeCodec(final DataSchemaNode schema, final UnionTypeDefinition type,
119                                                   final NamespaceContext namespaceContext) {
120         final XmlCodec<Object> xmlStringUnionCodec = new XmlStringUnionCodec(schema, type, this, namespaceContext);
121         return xmlStringUnionCodec;
122     }
123
124     private XmlCodec<?> createFromSimpleType(
125         final DataSchemaNode schema, final TypeDefinition<?> type,
126         final NamespaceContext namespaceContext) {
127         if (type instanceof InstanceIdentifierTypeDefinition) {
128             final XmlCodec<YangInstanceIdentifier> iidCodec = new XmlStringInstanceIdentifierCodec(schemaContext, this,
129                     namespaceContext);
130             return iidCodec;
131         }
132         if (type instanceof EmptyTypeDefinition) {
133             return XmlEmptyCodec.INSTANCE;
134         }
135
136         final TypeDefinitionAwareCodec<Object, ?> codec = TypeDefinitionAwareCodec.from(type);
137         if (codec == null) {
138             LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName().getLocalName());
139             return NULL_CODEC;
140         }
141         return AbstractXmlCodec.create(codec);
142     }
143
144     SchemaContext getSchemaContext() {
145         return schemaContext;
146     }
147
148     XmlCodec<?> codecFor(final DataSchemaNode schema, final NamespaceContext namespaceContext) {
149         Preconditions.checkArgument(schema instanceof TypedSchemaNode, "Unsupported node type %s", schema.getClass());
150         return codecs.getUnchecked(new SimpleImmutableEntry<>((TypedSchemaNode)schema, namespaceContext));
151     }
152
153     XmlCodec<?> codecFor(final DataSchemaNode schema, final TypeDefinition<?> unionSubType,
154                          final NamespaceContext namespaceContext) {
155         return createCodec(schema, unionSubType, namespaceContext);
156     }
157 }