Initial code drop of yang model driven configuration system
[controller.git] / opendaylight / config / yang-jmx-generator / src / main / java / org / opendaylight / controller / config / yangjmxgenerator / ServiceInterfaceEntry.java
1 /*
2  * Copyright (c) 2013 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 package org.opendaylight.controller.config.yangjmxgenerator;
9
10 import static com.google.common.base.Preconditions.checkNotNull;
11 import static java.lang.String.format;
12 import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.SERVICE_TYPE_Q_NAME;
13
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.Module;
25 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 import com.google.common.base.Optional;
30
31 /**
32  * Represents identity derived from {@link ConfigConstants#SERVICE_TYPE_Q_NAME}.
33  * Example:
34  * <p>
35  * <blockquote>
36  *
37  * <pre>
38  *  identity eventbus {
39  *  description
40  *  "Service representing an event bus. The service acts as message
41  *  router between event producers and event consumers";
42  *
43  *  base "config:service-type";
44  *  config:java-class "com.google.common.eventbus.EventBus";
45  *  }
46  * </pre>
47  *
48  * </blockquote>
49  * </p>
50  */
51 public class ServiceInterfaceEntry extends AbstractEntry {
52     private static final Logger logger = LoggerFactory
53             .getLogger(ServiceInterfaceEntry.class);
54
55     private static final String CLASS_NAME_SUFFIX = "ServiceInterface";
56     private final Optional<ServiceInterfaceEntry> maybeBaseCache;
57     private final String exportedOsgiClassName;
58     private final QName qName;
59     private final String nullableDescription, packageName, typeName;
60
61     private ServiceInterfaceEntry(IdentitySchemaNode id, String packageName) {
62         this(Optional.<ServiceInterfaceEntry> absent(), id, packageName);
63     }
64
65     private ServiceInterfaceEntry(Optional<ServiceInterfaceEntry> base,
66             IdentitySchemaNode id, String packageName) {
67         checkNotNull(base);
68         this.maybeBaseCache = base;
69         List<UnknownSchemaNode> unknownSchemaNodes = id.getUnknownSchemaNodes();
70         List<String> exportedOsgiClassNames = new ArrayList<>(
71                 unknownSchemaNodes.size());
72         for (UnknownSchemaNode usn : unknownSchemaNodes) {
73             if (ConfigConstants.JAVA_CLASS_EXTENSION_QNAME.equals(usn
74                     .getNodeType())) {
75                 String localName = usn.getNodeParameter();
76                 exportedOsgiClassNames.add(localName);
77             } else {
78                 throw new IllegalStateException(format(
79                         "Unexpected unknown schema node. Expected %s, got %s",
80                         ConfigConstants.JAVA_CLASS_EXTENSION_QNAME,
81                         usn.getNodeType()));
82             }
83         }
84         if (exportedOsgiClassNames.size() != 1) {
85             throw new IllegalArgumentException(
86                     format("Cannot find one to one mapping from %s to "
87                             + "java class defined by %s language extension in %s",
88                             getClass(),
89                             ConfigConstants.JAVA_CLASS_EXTENSION_QNAME, id));
90         }
91         this.exportedOsgiClassName = exportedOsgiClassNames.get(0);
92         qName = id.getQName();
93         nullableDescription = id.getDescription();
94         typeName = getSimpleName(exportedOsgiClassName) + CLASS_NAME_SUFFIX;
95         this.packageName = packageName;
96     }
97
98     private static final String getSimpleName(String fullyQualifiedName) {
99         int lastDotPosition = fullyQualifiedName.lastIndexOf(".");
100         return fullyQualifiedName.substring(lastDotPosition + 1);
101     }
102
103     public String getNullableDescription() {
104         return nullableDescription;
105     }
106
107     public Optional<ServiceInterfaceEntry> getBase() {
108         return maybeBaseCache;
109     }
110
111     public String getExportedOsgiClassName() {
112         return exportedOsgiClassName;
113     }
114
115     public QName getQName() {
116         return qName;
117     }
118
119     /**
120      * @return Map of QNames as keys and ServiceInterfaceEntry instances as
121      *         values
122      */
123     public static Map<QName, ServiceInterfaceEntry> create(Module module,
124             String packageName) {
125         logger.debug("Generating ServiceInterfaces from {} to package {}",
126                 module.getNamespace(), packageName);
127
128         Map<IdentitySchemaNode, ServiceInterfaceEntry> identitiesToSIs = new HashMap<>();
129         Set<IdentitySchemaNode> notVisited = new HashSet<>(
130                 module.getIdentities());
131         int lastSize = notVisited.size() + 1;
132         while (notVisited.size() > 0) {
133             if (notVisited.size() == lastSize) {
134                 logger.debug(
135                         "Following identities will be ignored while generating ServiceInterfaces, as they are not derived from {} : {}",
136                         SERVICE_TYPE_Q_NAME, notVisited);
137                 break;
138             }
139             lastSize = notVisited.size();
140             for (Iterator<IdentitySchemaNode> iterator = notVisited.iterator(); iterator
141                     .hasNext();) {
142                 IdentitySchemaNode identity = iterator.next();
143                 ServiceInterfaceEntry created = null;
144                 if (identity.getBaseIdentity() == null) {
145                     // this can happen while loading config module, just skip
146                     // the identity
147                     continue;
148                 } else if (identity.getBaseIdentity().getQName()
149                         .equals(SERVICE_TYPE_Q_NAME)) {
150                     // this is a base type
151                     created = new ServiceInterfaceEntry(identity, packageName);
152                 } else {
153                     ServiceInterfaceEntry foundBase = identitiesToSIs
154                             .get(identity.getBaseIdentity());
155                     // derived type, did we convert the parent?
156                     if (foundBase != null) {
157                         created = new ServiceInterfaceEntry(
158                                 Optional.of(foundBase), identity, packageName);
159                     }
160                 }
161                 if (created != null) {
162                     created.setYangModuleName(module.getName());
163                     // TODO how to get local name
164                     created.setYangModuleLocalname(identity.getQName()
165                             .getLocalName());
166                     identitiesToSIs.put(identity, created);
167                     iterator.remove();
168                 }
169             }
170         }
171         // create result map
172         Map<QName, ServiceInterfaceEntry> resultMap = new HashMap<>();
173         for (ServiceInterfaceEntry sie : identitiesToSIs.values()) {
174             resultMap.put(sie.getQName(), sie);
175         }
176         logger.debug("Number of ServiceInterfaces to be generated: {}",
177                 resultMap.size());
178         return resultMap;
179     }
180
181     public String getFullyQualifiedName() {
182         return packageName + "." + typeName;
183     }
184
185     public String getPackageName() {
186         return packageName;
187     }
188
189     public String getTypeName() {
190         return typeName;
191     }
192
193     @Override
194     public boolean equals(Object o) {
195         if (this == o)
196             return true;
197         if (o == null || getClass() != o.getClass())
198             return false;
199
200         ServiceInterfaceEntry that = (ServiceInterfaceEntry) o;
201
202         if (!maybeBaseCache.equals(that.maybeBaseCache))
203             return false;
204         if (!nullableDescription.equals(that.nullableDescription))
205             return false;
206         if (!exportedOsgiClassName.equals(that.exportedOsgiClassName))
207             return false;
208         if (!qName.equals(that.qName))
209             return false;
210         if (!packageName.equals(that.packageName))
211             return false;
212         if (!typeName.equals(that.typeName))
213             return false;
214
215         return true;
216     }
217
218     @Override
219     public int hashCode() {
220         int result = maybeBaseCache.hashCode();
221         result = 31 * result + exportedOsgiClassName.hashCode();
222         result = 31 * result + nullableDescription.hashCode();
223         result = 31 * result + typeName.hashCode();
224         result = 31 * result + packageName.hashCode();
225         result = 31 * result + qName.hashCode();
226         return result;
227     }
228
229     @Override
230     public String toString() {
231         return "ServiceInterfaceEntry{" + "maybeBaseCache=" + maybeBaseCache
232                 + ", qName='" + qName + '\'' + ", fullyQualifiedName='"
233                 + getFullyQualifiedName() + '\'' + ", exportedOsgiClassName="
234                 + exportedOsgiClassName + ", nullableDescription='"
235                 + nullableDescription + '\'' + '}';
236     }
237 }