2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.binding.data.codec.impl;
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.cache.CacheBuilder;
13 import com.google.common.cache.CacheLoader;
14 import com.google.common.cache.LoadingCache;
15 import org.opendaylight.yangtools.util.ClassLoaderUtils;
16 import org.opendaylight.yangtools.yang.binding.BindingMapping;
17 import org.opendaylight.yangtools.yang.binding.ChildOf;
18 import org.opendaylight.yangtools.yang.binding.DataContainer;
19 import org.opendaylight.yangtools.yang.binding.DataRoot;
20 import org.opendaylight.yangtools.yang.binding.Notification;
21 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.common.QNameModule;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
25 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
26 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
29 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
31 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
32 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
33 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
34 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
35 import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
37 final class SchemaRootCodecContext extends DataContainerCodecContext<SchemaContext> {
39 private final LoadingCache<Class<?>, DataContainerCodecContext<?>> childrenByClass = CacheBuilder.newBuilder()
40 .build(new CacheLoader<Class<?>, DataContainerCodecContext<?>>() {
42 public DataContainerCodecContext<?> load(final Class<?> key) {
43 return createDataTreeChildContext(key);
48 private final LoadingCache<Class<?>, ContainerNodeCodecContext> rpcDataByClass = CacheBuilder.newBuilder().build(
49 new CacheLoader<Class<?>, ContainerNodeCodecContext>() {
51 public ContainerNodeCodecContext load(final Class<?> key) {
52 return createRpcDataContext(key);
56 private final LoadingCache<Class<?>, NotificationCodecContext> notificationsByClass = CacheBuilder.newBuilder()
57 .build(new CacheLoader<Class<?>, NotificationCodecContext>() {
59 public NotificationCodecContext load(final Class<?> key) {
60 return createNotificationDataContext(key);
64 private final LoadingCache<QName, DataContainerCodecContext<?>> childrenByQName = CacheBuilder.newBuilder().build(
65 new CacheLoader<QName, DataContainerCodecContext<?>>() {
67 public DataContainerCodecContext<?> load(final QName qname) {
68 final DataSchemaNode childSchema = schema().getDataChildByName(qname);
69 Preconditions.checkArgument(childSchema != null, "Argument %s is not valid child of %s", qname,
72 if (childSchema instanceof DataNodeContainer || childSchema instanceof ChoiceSchemaNode) {
73 final Class<?> childCls = factory().getRuntimeContext().getClassForSchema(childSchema);
74 return getStreamChild(childCls);
76 throw new UnsupportedOperationException("Unsupported child type " + childSchema.getClass());
81 private final LoadingCache<SchemaPath, ContainerNodeCodecContext> rpcDataByPath = CacheBuilder.newBuilder().build(
82 new CacheLoader<SchemaPath, ContainerNodeCodecContext>() {
84 @SuppressWarnings({ "rawtypes", "unchecked" })
86 public ContainerNodeCodecContext load(final SchemaPath key) {
87 final ContainerSchemaNode schema = SchemaContextUtil.getRpcDataSchema(schema(), key);
88 final Class cls = factory().getRuntimeContext().getClassForSchema(schema);
93 private final LoadingCache<SchemaPath, NotificationCodecContext> notificationsByPath = CacheBuilder.newBuilder()
94 .build(new CacheLoader<SchemaPath, NotificationCodecContext>() {
96 @SuppressWarnings({ "rawtypes", "unchecked" })
98 public NotificationCodecContext load(final SchemaPath key) throws Exception {
99 final NotificationDefinition schema = SchemaContextUtil.getNotificationSchema(schema(), key);
100 final Class clz = factory().getRuntimeContext().getClassForSchema(schema);
101 return getNotification(clz);
105 private SchemaRootCodecContext(final DataContainerCodecPrototype<SchemaContext> dataPrototype) {
106 super(dataPrototype);
110 * Creates RootNode from supplied CodecContextFactory.
113 * CodecContextFactory
116 static SchemaRootCodecContext create(final CodecContextFactory factory) {
117 final DataContainerCodecPrototype<SchemaContext> prototype = DataContainerCodecPrototype.rootPrototype(factory);
118 return new SchemaRootCodecContext(prototype);
122 protected DataContainerCodecContext<?> getStreamChild(final Class<?> childClass) {
123 return childrenByClass.getUnchecked(childClass);
127 protected Optional<DataContainerCodecContext<?>> getPossibleStreamChild(final Class<?> childClass) {
128 throw new UnsupportedOperationException("Not supported");
132 protected NodeCodecContext getYangIdentifierChild(final PathArgument arg) {
133 return childrenByQName.getUnchecked(arg.getNodeType());
137 protected Object dataFromNormalizedNode(final NormalizedNode<?, ?> normalizedNode) {
138 throw new UnsupportedOperationException("Could not create Binding data representation for root");
141 ContainerNodeCodecContext getRpc(final Class<? extends DataContainer> rpcInputOrOutput) {
142 return rpcDataByClass.getUnchecked(rpcInputOrOutput);
145 NotificationCodecContext getNotification(final Class<? extends Notification> notification) {
146 return notificationsByClass.getUnchecked(notification);
149 NotificationCodecContext getNotification(final SchemaPath notification) {
150 return notificationsByPath.getUnchecked(notification);
153 ContainerNodeCodecContext getRpc(final SchemaPath notification) {
154 return rpcDataByPath.getUnchecked(notification);
157 private DataContainerCodecContext<?> createDataTreeChildContext(final Class<?> key) {
158 final Class<Object> parent = ClassLoaderUtils.findFirstGenericArgument(key, ChildOf.class);
159 Preconditions.checkArgument(DataRoot.class.isAssignableFrom(parent));
160 final QName qname = BindingReflections.findQName(key);
161 final DataSchemaNode childSchema = schema().getDataChildByName(qname);
162 return DataContainerCodecPrototype.from(key, childSchema, factory()).get();
165 private ContainerNodeCodecContext createRpcDataContext(final Class<?> key) {
166 Preconditions.checkArgument(DataContainer.class.isAssignableFrom(key));
167 final QName qname = BindingReflections.findQName(key);
168 final QNameModule module = qname.getModule();
169 RpcDefinition rpc = null;
170 for (final RpcDefinition potential : schema().getOperations()) {
171 final QName potentialQName = potential.getQName();
174 * Check if rpc and class represents data from same module and then
175 * checks if rpc local name produces same class name as class name
176 * appended with Input/Output based on QName associated with bidning
179 * FIXME: Rework this to have more precise logic regarding Binding
182 if (module.equals(potentialQName.getModule())
183 && key.getSimpleName().equals(
184 BindingMapping.getClassName(potentialQName) + BindingMapping.getClassName(qname))) {
189 Preconditions.checkArgument(rpc != null, "Supplied class %s is not valid RPC class.", key);
190 final ContainerSchemaNode schema = SchemaNodeUtils.getRpcDataSchema(rpc, qname);
191 Preconditions.checkArgument(schema != null, "Schema for %s does not define input / output.", rpc.getQName());
192 return (ContainerNodeCodecContext) DataContainerCodecPrototype.from(key, schema, factory()).get();
195 private NotificationCodecContext createNotificationDataContext(final Class<?> notificationType) {
196 Preconditions.checkArgument(Notification.class.isAssignableFrom(notificationType));
197 Preconditions.checkArgument(notificationType.isInterface(), "Supplied class must be interface.");
198 final QName qname = BindingReflections.findQName(notificationType);
200 * FIXME: After Lithium cleanup of yang-model-api, use direct call on schema context
201 * to retrieve notification via index.
203 final NotificationDefinition schema = SchemaContextUtil.getNotificationSchema(schema(),
204 SchemaPath.create(true, qname));
205 Preconditions.checkArgument(schema != null, "Supplied %s is not valid notification", notificationType);
207 return new NotificationCodecContext(notificationType, schema, factory());