d1746e3ce49176ecf9d799472e4f8f015c80383b
[smo/teiv.git] /
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2024 Ericsson
4  *  Modifications Copyright (C) 2024 OpenInfra Foundation Europe
5  *  ================================================================================
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21 package org.oran.smo.yangtools.parser.model.statements;
22
23 import java.lang.reflect.Constructor;
24 import java.util.HashMap;
25 import java.util.Map;
26
27 import org.oran.smo.yangtools.parser.ParserExecutionContext;
28 import org.oran.smo.yangtools.parser.findings.Finding;
29 import org.oran.smo.yangtools.parser.findings.ParserFindingType;
30 import org.oran.smo.yangtools.parser.model.ModuleIdentity;
31 import org.oran.smo.yangtools.parser.model.statements.yang.YangCoreClassSupplier;
32 import org.oran.smo.yangtools.parser.model.yangdom.YangDomElement;
33 import org.oran.smo.yangtools.parser.util.StackTraceHelper;
34
35 /**
36  * Factory for creating instances of type-safe classes representing Yang statements.
37  *
38  * @author Mark Hollmann
39  */
40 public abstract class StatementFactory {
41
42     private static final Map<String, Constructor<? extends AbstractStatement>> CoreTypesConstructors = new HashMap<>();
43
44     private static final YangCoreClassSupplier yangCoreClassSupplier = new YangCoreClassSupplier();
45
46     /**
47      * Creates an instance of a core Yang statement.
48      */
49     @SuppressWarnings("unchecked")
50     public static <T extends AbstractStatement> T createYangCoreStatement(final ParserExecutionContext context,
51             final YangDomElement domElement, final AbstractStatement parentStatement) {
52
53         final String yangStatementName = domElement.getName();
54         try {
55             if (!CoreTypesConstructors.containsKey(yangStatementName)) {
56                 final Class<? extends AbstractStatement> clazz = yangCoreClassSupplier.getJavaClazzForStatement(null,
57                         yangStatementName);
58                 final Constructor<? extends AbstractStatement> constructor = clazz.getConstructor(AbstractStatement.class,
59                         YangDomElement.class);
60                 CoreTypesConstructors.put(yangStatementName, constructor);
61             }
62
63             final Constructor<? extends AbstractStatement> constructor = CoreTypesConstructors.get(yangStatementName);
64             final AbstractStatement newInstance = constructor.newInstance(parentStatement, domElement);
65
66             return (T) newInstance;
67
68         } catch (final Exception ex) {
69             context.addFinding(new Finding(domElement, ParserFindingType.P015_INVALID_SYNTAX_IN_DOCUMENT.toString(),
70                     "Not a valid YANG statement: " + yangStatementName));
71         }
72
73         return null;
74     }
75
76     /**
77      * Creates an instance of an extension statement.
78      */
79     @SuppressWarnings("unchecked")
80     public static <T extends ExtensionStatement> T createYangExtensionStatement(final ParserExecutionContext context,
81             final YangDomElement domNode, final AbstractStatement parentStatement) {
82
83         final String[] prefixAndStatement = domNode.getName().split(":");
84         final String prefix = prefixAndStatement[0];
85         final String extStatementName = prefixAndStatement[1];
86
87         final ModuleIdentity moduleIdentity = parentStatement.getPrefixResolver().getModuleForPrefix(prefix);
88         if (moduleIdentity == null) {
89             context.addFinding(new Finding(domNode, ParserFindingType.P033_UNRESOLVEABLE_PREFIX.toString(),
90                     "Prefix '" + prefixAndStatement[0] + "' for the extension statement not resolvable."));
91             return null;
92         }
93
94         final String extModuleName = moduleIdentity.getModuleName();
95
96         try {
97             final Class<? extends ExtensionStatement> clazz = getClassForYangExtension(context, extModuleName,
98                     extStatementName);
99             final Constructor<? extends ExtensionStatement> constructor = clazz.getConstructor(AbstractStatement.class,
100                     YangDomElement.class);
101
102             final ExtensionStatement newInstance = constructor.newInstance(parentStatement, domNode);
103
104             return (T) newInstance;
105         } catch (final NoSuchMethodException nsmex) {
106             context.addFinding(new Finding(domNode, ParserFindingType.P002_INVALID_EXTENSION_STATEMENT_CLASS.toString(),
107                     "Extension statement class constructor wrong."));
108         } catch (final Exception ex) {
109             context.addFinding(new Finding(domNode, ParserFindingType.P000_UNSPECIFIED_ERROR.toString(),
110                     "During instantiation of extension statement: " + ex.getMessage() + " - trace: " + StackTraceHelper
111                             .getStackTraceInfo(ex)));
112         }
113
114         return null;
115     }
116
117     @SuppressWarnings("unchecked")
118     public static <T extends AbstractStatement> T cloneYangStatement(final T statementToClone,
119             final AbstractStatement parentOfClone) {
120
121         final YangDomElement domElementOfStatementToClone = statementToClone.getDomElement();
122         final Class<? extends AbstractStatement> clazz = statementToClone.getClass();
123
124         try {
125             final Constructor<? extends AbstractStatement> constructor = clazz.getConstructor(AbstractStatement.class,
126                     YangDomElement.class);
127             final AbstractStatement newInstance = constructor.newInstance(parentOfClone, domElementOfStatementToClone);
128
129             return (T) newInstance;
130
131         } catch (final Exception ex) {
132             // no-op, cannot happen
133             return null;
134         }
135     }
136
137     /**
138      * Given the module name where an extension is defined, and the extension name, returns either a dedicated
139      * class that can handle the statement, or the catch-all {@link ExtensionStatement} class.
140      */
141     @SuppressWarnings("unchecked")
142     private static <T extends ExtensionStatement> Class<T> getClassForYangExtension(final ParserExecutionContext context,
143             final String moduleName, final String statementName) {
144
145         for (final StatementClassSupplier supplier : context.getExtensionCreators()) {
146             final Class<ExtensionStatement> extensionClazz = supplier.getJavaClazzForStatement(moduleName, statementName);
147             if (extensionClazz != null) {
148                 return (Class<T>) extensionClazz;
149             }
150         }
151
152         /*
153          * No supplier found that can handle the extensions, we return extension catch-all base class.
154          */
155         return (Class<T>) ExtensionStatement.class;
156     }
157 }