fe1573d6c648dc668633b32a69040d685bea1f1d
[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.base.Preconditions;
12 import com.google.common.base.Verify;
13 import com.google.common.cache.CacheBuilder;
14 import com.google.common.cache.CacheLoader;
15 import com.google.common.cache.LoadingCache;
16 import javax.xml.stream.XMLStreamException;
17 import javax.xml.stream.XMLStreamWriter;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
19 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
20 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
24 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
25 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
26 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
27 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
28 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
29 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
30 import org.opendaylight.yangtools.yang.model.util.type.DerivedTypes;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 public final class XmlCodecFactory {
35
36     private static final Logger LOG = LoggerFactory.getLogger(XmlCodecFactory.class);
37     private static final XmlCodec<Object> NULL_CODEC = new XmlCodec<Object>() {
38         @Override
39         public Object deserialize(final String input) {
40             return null;
41         }
42
43         @Override
44         public String serialize(final Object input) {
45             return null;
46         }
47
48         @Override
49         public void serializeToWriter(final XMLStreamWriter writer, final Object value) throws XMLStreamException {
50             // NOOP since codec is unkwown.
51             LOG.warn("Call of the serializeToWriter method on XmlCodecFactory.NULL_CODEC object. No operation " +
52                     "performed.");
53         }
54     };
55
56     private final LoadingCache<DataSchemaNode, XmlCodec<?>> codecs =
57             CacheBuilder.newBuilder().softValues().build(new CacheLoader<DataSchemaNode, XmlCodec<?>>() {
58                 @Override
59                 public XmlCodec<?> load(final DataSchemaNode key) throws Exception {
60                     final TypeDefinition<?> type;
61                     if (key instanceof LeafSchemaNode) {
62                         type = ((LeafSchemaNode) key).getType();
63                     } else if (key instanceof LeafListSchemaNode) {
64                         type = ((LeafListSchemaNode) key).getType();
65                     } else {
66                         throw new IllegalArgumentException("Not supported node type " + key.getClass().getName());
67                     }
68                     return createCodec(key,type);
69                 }
70             });
71
72     private final SchemaContext schemaContext;
73     private final XmlCodec<YangInstanceIdentifier> iidCodec;
74
75     private XmlCodecFactory(final SchemaContext context) {
76         this.schemaContext = Preconditions.checkNotNull(context);
77         iidCodec = new XmlStringInstanceIdentifierCodec(context, this);
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 TypeDefinition<?> normalizedType = DerivedTypes.derivedTypeBuilder(type, type.getPath()).build();
92         if (normalizedType instanceof LeafrefTypeDefinition) {
93             return createReferencedTypeCodec(key, (LeafrefTypeDefinition) normalizedType);
94         } else if (normalizedType instanceof IdentityrefTypeDefinition) {
95             final XmlCodec<?> xmlStringIdentityrefCodec =
96                     new XmlStringIdentityrefCodec(schemaContext, key.getQName().getModule());
97             return xmlStringIdentityrefCodec;
98         }
99         return createFromSimpleType(normalizedType);
100     }
101
102     private XmlCodec<?> createReferencedTypeCodec(final DataSchemaNode schema, final LeafrefTypeDefinition type) {
103         // FIXME: Verify if this does indeed support leafref of leafref
104         final TypeDefinition<?> referencedType =
105                 SchemaContextUtil.getBaseTypeForLeafRef(type, getSchemaContext(), schema);
106         Verify.verifyNotNull(referencedType, "Unable to find base type for leafref node '%s'.", schema.getPath());
107         return createCodec(schema, referencedType);
108     }
109
110     private XmlCodec<?> createFromSimpleType(final TypeDefinition<?> type) {
111         if (type instanceof InstanceIdentifierTypeDefinition) {
112             return iidCodec;
113         }
114         if (type instanceof EmptyTypeDefinition) {
115             return XmlEmptyCodec.INSTANCE;
116         }
117
118         final TypeDefinitionAwareCodec<Object, ?> codec = TypeDefinitionAwareCodec.from(type);
119         if (codec == null) {
120             LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName().getLocalName());
121             return NULL_CODEC;
122         }
123         return AbstractXmlCodec.create(codec);
124     }
125
126     SchemaContext getSchemaContext() {
127         return schemaContext;
128     }
129
130     XmlCodec<?> codecFor(final DataSchemaNode schema) {
131         return codecs.getUnchecked(schema);
132     }
133 }