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