91899301aa6879c3ce82a390b7757d7546540ba5
[controller.git] / opendaylight / blueprint / src / main / java / org / opendaylight / controller / blueprint / ext / BindingContext.java
1 /*
2  * Copyright (c) 2016 Brocade Communications 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 package org.opendaylight.controller.blueprint.ext;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Strings;
12 import java.lang.reflect.InvocationTargetException;
13 import java.util.Collections;
14 import java.util.List;
15 import org.opendaylight.yangtools.yang.binding.DataObject;
16 import org.opendaylight.yangtools.yang.binding.Identifiable;
17 import org.opendaylight.yangtools.yang.binding.Identifier;
18 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
19 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
22 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
23 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
24 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
27 import org.osgi.service.blueprint.container.ComponentDefinitionException;
28 import org.w3c.dom.Element;
29
30 /**
31  * Base class to abstract binding type-specific behavior.
32  *
33  * @author Thomas Pantelis (originally; re-factored by Michael Vorburger.ch)
34  */
35 public abstract class BindingContext {
36
37     public static BindingContext create(String logName, Class<? extends DataObject> klass,
38             String appConfigListKeyValue) {
39         if (Identifiable.class.isAssignableFrom(klass)) {
40             // The binding class corresponds to a yang list.
41             if (Strings.isNullOrEmpty(appConfigListKeyValue)) {
42                 throw new ComponentDefinitionException(String.format(
43                         "%s: App config binding class %s represents a yang list therefore \"%s\" must be specified",
44                         logName, klass.getName(), DataStoreAppConfigMetadata.LIST_KEY_VALUE));
45             }
46
47             try {
48                 return ListBindingContext.newInstance(klass, appConfigListKeyValue);
49             } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
50                     | InvocationTargetException | NoSuchMethodException | SecurityException e) {
51                 throw new ComponentDefinitionException(String.format(
52                         "%s: Error initializing for app config list binding class %s",
53                         logName, klass.getName()), e);
54             }
55
56         } else {
57             return new ContainerBindingContext(klass);
58         }
59     }
60
61     public final InstanceIdentifier<DataObject> appConfigPath;
62     public final Class<DataObject> appConfigBindingClass;
63     public final Class<? extends DataSchemaNode> schemaType;
64     public final QName bindingQName;
65
66     private BindingContext(final Class<DataObject> appConfigBindingClass,
67             final InstanceIdentifier<DataObject> appConfigPath, final Class<? extends DataSchemaNode> schemaType) {
68         this.appConfigBindingClass = appConfigBindingClass;
69         this.appConfigPath = appConfigPath;
70         this.schemaType = schemaType;
71
72         bindingQName = BindingReflections.findQName(appConfigBindingClass);
73     }
74
75     public abstract NormalizedNode<?, ?> parseDataElement(Element element, DataSchemaNode dataSchema,
76             DomToNormalizedNodeParserFactory parserFactory);
77
78     public abstract NormalizedNode<?, ?> newDefaultNode(DataSchemaNode dataSchema);
79
80     /**
81      * BindingContext implementation for a container binding.
82      */
83     private static class ContainerBindingContext extends BindingContext {
84         @SuppressWarnings("unchecked")
85         ContainerBindingContext(final Class<? extends DataObject> appConfigBindingClass) {
86             super((Class<DataObject>) appConfigBindingClass,
87                     InstanceIdentifier.create((Class<DataObject>) appConfigBindingClass), ContainerSchemaNode.class);
88         }
89
90         @Override
91         public NormalizedNode<?, ?> newDefaultNode(final DataSchemaNode dataSchema) {
92             return ImmutableNodes.containerNode(bindingQName);
93         }
94
95         @Override
96         public NormalizedNode<?, ?> parseDataElement(final Element element, final DataSchemaNode dataSchema,
97                 final DomToNormalizedNodeParserFactory parserFactory) {
98             return parserFactory.getContainerNodeParser().parse(Collections.singletonList(element),
99                     (ContainerSchemaNode)dataSchema);
100         }
101     }
102
103     /**
104      * BindingContext implementation for a list binding.
105      */
106     private static class ListBindingContext extends BindingContext {
107         final String appConfigListKeyValue;
108
109         @SuppressWarnings("unchecked")
110         ListBindingContext(final Class<? extends DataObject> appConfigBindingClass,
111                 final InstanceIdentifier<? extends DataObject> appConfigPath, final String appConfigListKeyValue) {
112             super((Class<DataObject>) appConfigBindingClass, (InstanceIdentifier<DataObject>) appConfigPath,
113                     ListSchemaNode.class);
114             this.appConfigListKeyValue = appConfigListKeyValue;
115         }
116
117         @SuppressWarnings({ "rawtypes", "unchecked" })
118         private static ListBindingContext newInstance(final Class<? extends DataObject> bindingClass,
119                 final String listKeyValue) throws InstantiationException, IllegalAccessException,
120                 IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
121             // We assume the yang list key type is string.
122             Identifier keyInstance = (Identifier) bindingClass.getMethod("getKey").getReturnType()
123                     .getConstructor(String.class).newInstance(listKeyValue);
124             InstanceIdentifier appConfigPath = InstanceIdentifier.builder((Class)bindingClass, keyInstance).build();
125             return new ListBindingContext(bindingClass, appConfigPath, listKeyValue);
126         }
127
128         @Override
129         public NormalizedNode<?, ?> newDefaultNode(final DataSchemaNode dataSchema) {
130             // We assume there's only one key for the list.
131             List<QName> keys = ((ListSchemaNode)dataSchema).getKeyDefinition();
132             Preconditions.checkArgument(keys.size() == 1, "Expected only 1 key for list %s", appConfigBindingClass);
133             QName listKeyQName = keys.get(0);
134             return ImmutableNodes.mapEntryBuilder(bindingQName, listKeyQName, appConfigListKeyValue).build();
135         }
136
137         @Override
138         public NormalizedNode<?, ?> parseDataElement(final Element element, final DataSchemaNode dataSchema,
139                 final DomToNormalizedNodeParserFactory parserFactory) {
140             return parserFactory.getMapEntryNodeParser().parse(Collections.singletonList(element),
141                     (ListSchemaNode)dataSchema);
142         }
143     }
144 }