Update ParserListenerUtils to use SchemaPath
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / YangParserListenerImpl.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.yangtools.yang.parser.impl;
9
10 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.checkMissingBody;
11 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.createListKey;
12 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getConfig;
13 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getIdentityrefBase;
14 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.parseConstraints;
15 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.parseDefault;
16 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.parseRefine;
17 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.parseSchemaNodeArgs;
18 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.parseStatus;
19 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.parseTypeWithBody;
20 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.parseUnits;
21 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.parseUnknownTypeWithBody;
22 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.parseUserOrdered;
23 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.parseYinValue;
24 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.stringFromNode;
25
26 import com.google.common.base.Splitter;
27 import com.google.common.base.Strings;
28 import com.google.common.collect.Iterables;
29
30 import java.net.URI;
31 import java.text.DateFormat;
32 import java.text.ParseException;
33 import java.text.SimpleDateFormat;
34 import java.util.Date;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Stack;
38
39 import org.antlr.v4.runtime.tree.ParseTree;
40 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
41 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Argument_stmtContext;
42 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Base_stmtContext;
43 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Contact_stmtContext;
44 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Container_stmtContext;
45 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Default_stmtContext;
46 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Description_stmtContext;
47 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Deviate_add_stmtContext;
48 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Deviate_delete_stmtContext;
49 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Deviate_not_supported_stmtContext;
50 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Deviate_replace_stmtContext;
51 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Import_stmtContext;
52 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Key_stmtContext;
53 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Leaf_list_stmtContext;
54 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Leaf_stmtContext;
55 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.List_stmtContext;
56 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_header_stmtsContext;
57 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Namespace_stmtContext;
58 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Ordered_by_stmtContext;
59 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Organization_stmtContext;
60 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Prefix_stmtContext;
61 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Presence_stmtContext;
62 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Reference_stmtContext;
63 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_date_stmtContext;
64 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtContext;
65 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext;
66 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Status_stmtContext;
67 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Type_body_stmtsContext;
68 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Units_stmtContext;
69 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.When_stmtContext;
70 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Yang_version_stmtContext;
71 import org.opendaylight.yangtools.antlrv4.code.gen.YangParserBaseListener;
72 import org.opendaylight.yangtools.yang.common.QName;
73 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
74 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
75 import org.opendaylight.yangtools.yang.model.util.BaseTypes;
76 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
77 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
78 import org.opendaylight.yangtools.yang.parser.builder.api.ExtensionBuilder;
79 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
80 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
81 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
82 import org.opendaylight.yangtools.yang.parser.builder.impl.AnyXmlBuilder;
83 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
84 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
85 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
86 import org.opendaylight.yangtools.yang.parser.builder.impl.DeviationBuilder;
87 import org.opendaylight.yangtools.yang.parser.builder.impl.FeatureBuilder;
88 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
89 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafListSchemaNodeBuilder;
90 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
91 import org.opendaylight.yangtools.yang.parser.builder.impl.ListSchemaNodeBuilder;
92 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
93 import org.opendaylight.yangtools.yang.parser.builder.impl.NotificationBuilder;
94 import org.opendaylight.yangtools.yang.parser.builder.impl.RefineHolderImpl;
95 import org.opendaylight.yangtools.yang.parser.builder.impl.RpcDefinitionBuilder;
96 import org.opendaylight.yangtools.yang.parser.builder.impl.UnionTypeBuilder;
97 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilderImpl;
98 import org.slf4j.Logger;
99 import org.slf4j.LoggerFactory;
100
101 public final class YangParserListenerImpl extends YangParserBaseListener {
102     private static final Logger LOGGER = LoggerFactory.getLogger(YangParserListenerImpl.class);
103     private static final Splitter COLON_SPLITTER = Splitter.on(':');
104     private static final String AUGMENT_STR = "augment";
105
106     private final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
107     private final Stack<Stack<QName>> actualPath = new Stack<>();
108     private final String sourcePath;
109     private ModuleBuilder moduleBuilder;
110     private String moduleName;
111     private URI namespace;
112     private String yangModelPrefix;
113     private Date revision = new Date(0L);
114     private int augmentOrder;
115
116     private void addNodeToPath(final QName name) {
117         actualPath.peek().push(name);
118     }
119
120     private QName removeNodeFromPath() {
121         return actualPath.peek().pop();
122     }
123
124     public YangParserListenerImpl(final String sourcePath) {
125         this.sourcePath = sourcePath;
126     }
127
128     @Override
129     public void enterModule_stmt(final YangParser.Module_stmtContext ctx) {
130         moduleName = stringFromNode(ctx);
131         LOGGER.trace("entering module {}", moduleName);
132         enterLog("module", moduleName, 0);
133         actualPath.push(new Stack<QName>());
134
135         moduleBuilder = new ModuleBuilder(moduleName, sourcePath);
136
137         String description = null;
138         String reference = null;
139         for (int i = 0; i < ctx.getChildCount(); i++) {
140             ParseTree child = ctx.getChild(i);
141             if (child instanceof Description_stmtContext) {
142                 description = stringFromNode(child);
143             } else if (child instanceof Reference_stmtContext) {
144                 reference = stringFromNode(child);
145             } else {
146                 if (description != null && reference != null) {
147                     break;
148                 }
149             }
150         }
151         moduleBuilder.setDescription(description);
152         moduleBuilder.setReference(reference);
153     }
154
155     @Override
156     public void exitModule_stmt(final YangParser.Module_stmtContext ctx) {
157         exitLog("module");
158         actualPath.pop();
159     }
160
161     @Override public void enterSubmodule_stmt(final YangParser.Submodule_stmtContext ctx) {
162         moduleName = stringFromNode(ctx);
163         LOGGER.trace("entering submodule {}", moduleName);
164         enterLog("submodule", moduleName, 0);
165         actualPath.push(new Stack<QName>());
166
167         moduleBuilder = new ModuleBuilder(moduleName, true, sourcePath);
168
169         String description = null;
170         String reference = null;
171         for (int i = 0; i < ctx.getChildCount(); i++) {
172             ParseTree child = ctx.getChild(i);
173             if (child instanceof Description_stmtContext) {
174                 description = stringFromNode(child);
175             } else if (child instanceof Reference_stmtContext) {
176                 reference = stringFromNode(child);
177             } else {
178                 if (description != null && reference != null) {
179                     break;
180                 }
181             }
182         }
183         moduleBuilder.setDescription(description);
184         moduleBuilder.setReference(reference);
185     }
186
187     @Override
188     public void exitSubmodule_stmt(final YangParser.Submodule_stmtContext ctx) {
189         exitLog("submodule");
190         actualPath.pop();
191     }
192
193     @Override
194     public void enterBelongs_to_stmt(final YangParser.Belongs_to_stmtContext ctx) {
195         moduleBuilder.setBelongsTo(stringFromNode(ctx));
196     }
197
198     @Override
199     public void enterModule_header_stmts(final Module_header_stmtsContext ctx) {
200         enterLog("module_header", "", ctx.getStart().getLine());
201         String yangVersion = null;
202         for (int i = 0; i < ctx.getChildCount(); ++i) {
203             final ParseTree treeNode = ctx.getChild(i);
204             if (treeNode instanceof Namespace_stmtContext) {
205                 final String namespaceStr = stringFromNode(treeNode);
206                 namespace = URI.create(namespaceStr);
207                 moduleBuilder.setNamespace(namespace);
208                 setLog("namespace", namespaceStr);
209             } else if (treeNode instanceof Prefix_stmtContext) {
210                 yangModelPrefix = stringFromNode(treeNode);
211                 moduleBuilder.setPrefix(yangModelPrefix);
212                 setLog("prefix", yangModelPrefix);
213             } else if (treeNode instanceof Yang_version_stmtContext) {
214                 yangVersion = stringFromNode(treeNode);
215                 setLog("yang-version", yangVersion);
216             }
217         }
218
219         if (yangVersion == null) {
220             yangVersion = "1";
221         }
222         moduleBuilder.setYangVersion(yangVersion);
223     }
224
225     @Override
226     public void exitModule_header_stmts(final Module_header_stmtsContext ctx) {
227         exitLog("module_header");
228     }
229
230     @Override
231     public void enterMeta_stmts(final YangParser.Meta_stmtsContext ctx) {
232         enterLog("meta_stmt", "", ctx.getStart().getLine());
233         for (int i = 0; i < ctx.getChildCount(); i++) {
234             ParseTree child = ctx.getChild(i);
235             if (child instanceof Organization_stmtContext) {
236                 final String organization = stringFromNode(child);
237                 moduleBuilder.setOrganization(organization);
238                 setLog("organization", organization);
239             } else if (child instanceof Contact_stmtContext) {
240                 final String contact = stringFromNode(child);
241                 moduleBuilder.setContact(contact);
242                 setLog("contact", contact);
243             } else if (child instanceof Description_stmtContext) {
244                 final String description = stringFromNode(child);
245                 moduleBuilder.setDescription(description);
246                 setLog("description", description);
247             } else if (child instanceof Reference_stmtContext) {
248                 final String reference = stringFromNode(child);
249                 moduleBuilder.setReference(reference);
250                 setLog("reference", reference);
251             }
252         }
253     }
254
255     @Override
256     public void exitMeta_stmts(final YangParser.Meta_stmtsContext ctx) {
257         exitLog("meta_stmt");
258     }
259
260     @Override
261     public void enterRevision_stmts(final Revision_stmtsContext ctx) {
262         enterLog("revisions", "", ctx.getStart().getLine());
263         for (int i = 0; i < ctx.getChildCount(); ++i) {
264             final ParseTree treeNode = ctx.getChild(i);
265             if (treeNode instanceof Revision_stmtContext) {
266                 updateRevisionForRevisionStatement(treeNode);
267             }
268         }
269     }
270
271     @Override
272     public void exitRevision_stmts(final Revision_stmtsContext ctx) {
273         exitLog("revisions");
274     }
275
276     private void updateRevisionForRevisionStatement(final ParseTree treeNode) {
277         final String revisionDateStr = stringFromNode(treeNode);
278         try {
279             final Date revisionDate = SIMPLE_DATE_FORMAT.parse(revisionDateStr);
280             if ((revisionDate != null) && (this.revision.compareTo(revisionDate) < 0)) {
281                 this.revision = revisionDate;
282                 moduleBuilder.setRevision(this.revision);
283                 setLog("revision", this.revision.toString());
284                 for (int i = 0; i < treeNode.getChildCount(); ++i) {
285                     ParseTree child = treeNode.getChild(i);
286                     if (child instanceof Reference_stmtContext) {
287                         moduleBuilder.setReference(stringFromNode(child));
288                     }
289                 }
290             }
291         } catch (ParseException e) {
292             final String message = "Failed to parse revision string: " + revisionDateStr;
293             LOGGER.warn(message);
294         }
295     }
296
297     @Override
298     public void enterImport_stmt(final Import_stmtContext ctx) {
299         final int line = ctx.getStart().getLine();
300         final String importName = stringFromNode(ctx);
301         enterLog("import", importName, line);
302
303         String importPrefix = null;
304         Date importRevision = null;
305
306         for (int i = 0; i < ctx.getChildCount(); ++i) {
307             final ParseTree treeNode = ctx.getChild(i);
308             if (treeNode instanceof Prefix_stmtContext) {
309                 importPrefix = stringFromNode(treeNode);
310             }
311             if (treeNode instanceof Revision_date_stmtContext) {
312                 String importRevisionStr = stringFromNode(treeNode);
313                 try {
314                     importRevision = SIMPLE_DATE_FORMAT.parse(importRevisionStr);
315                 } catch (ParseException e) {
316                     LOGGER.warn("Failed to parse import revision-date at line " + line + ": " + importRevisionStr);
317                 }
318             }
319         }
320         moduleBuilder.addModuleImport(importName, importRevision, importPrefix);
321         setLog("import", "(" + importName + "; " + importRevision + "; " + importPrefix + ")");
322     }
323
324     @Override
325     public void exitImport_stmt(final Import_stmtContext ctx) {
326         exitLog("import");
327     }
328
329     @Override
330     public void enterAugment_stmt(final YangParser.Augment_stmtContext ctx) {
331         final int line = ctx.getStart().getLine();
332         final String augmentPath = stringFromNode(ctx);
333         enterLog(AUGMENT_STR, augmentPath, line);
334         actualPath.push(new Stack<QName>());
335
336         AugmentationSchemaBuilder builder = moduleBuilder.addAugment(line, augmentPath, augmentOrder++);
337
338         for (int i = 0; i < ctx.getChildCount(); i++) {
339             ParseTree child = ctx.getChild(i);
340             if (child instanceof Description_stmtContext) {
341                 builder.setDescription(stringFromNode(child));
342             } else if (child instanceof Reference_stmtContext) {
343                 builder.setReference(stringFromNode(child));
344             } else if (child instanceof Status_stmtContext) {
345                 builder.setStatus(parseStatus((Status_stmtContext) child));
346             } else if (child instanceof When_stmtContext) {
347                 builder.addWhenCondition(stringFromNode(child));
348             }
349         }
350
351         moduleBuilder.enterNode(builder);
352     }
353
354     @Override
355     public void exitAugment_stmt(final YangParser.Augment_stmtContext ctx) {
356         moduleBuilder.exitNode();
357         exitLog(AUGMENT_STR);
358         actualPath.pop();
359     }
360
361     /**
362      * Create SchemaPath from actualPath and new node name.
363      *
364      * @param actualPath
365      *            current position in model
366      * @return SchemaPath object
367      */
368     private static SchemaPath createActualSchemaPath(final Stack<QName> actualPath) {
369         return SchemaPath.create(actualPath, true);
370     }
371
372     @Override
373     public void enterExtension_stmt(final YangParser.Extension_stmtContext ctx) {
374         final int line = ctx.getStart().getLine();
375         final String extName = stringFromNode(ctx);
376         enterLog("extension", extName, line);
377         QName qname = new QName(namespace, revision, yangModelPrefix, extName);
378         addNodeToPath(qname);
379         SchemaPath path = createActualSchemaPath(actualPath.peek());
380
381         ExtensionBuilder builder = moduleBuilder.addExtension(qname, line, path);
382         parseSchemaNodeArgs(ctx, builder);
383
384         String argument = null;
385         boolean yin = false;
386         for (int i = 0; i < ctx.getChildCount(); i++) {
387             ParseTree child = ctx.getChild(i);
388             if (child instanceof Argument_stmtContext) {
389                 argument = stringFromNode(child);
390                 yin = parseYinValue((Argument_stmtContext) child);
391                 break;
392             }
393         }
394         builder.setArgument(argument);
395         builder.setYinElement(yin);
396
397         moduleBuilder.enterNode(builder);
398     }
399
400     @Override
401     public void exitExtension_stmt(final YangParser.Extension_stmtContext ctx) {
402         moduleBuilder.exitNode();
403         exitLog("extension", removeNodeFromPath());
404     }
405
406     @Override
407     public void enterTypedef_stmt(final YangParser.Typedef_stmtContext ctx) {
408         final int line = ctx.getStart().getLine();
409         final String typedefName = stringFromNode(ctx);
410         enterLog("typedef", typedefName, line);
411         QName typedefQName = new QName(namespace, revision, yangModelPrefix, typedefName);
412         addNodeToPath(typedefQName);
413         SchemaPath path = createActualSchemaPath(actualPath.peek());
414
415         TypeDefinitionBuilder builder = moduleBuilder.addTypedef(line, typedefQName, path);
416         parseSchemaNodeArgs(ctx, builder);
417         builder.setUnits(parseUnits(ctx));
418         builder.setDefaultValue(parseDefault(ctx));
419
420         moduleBuilder.enterNode(builder);
421     }
422
423     @Override
424     public void exitTypedef_stmt(final YangParser.Typedef_stmtContext ctx) {
425         moduleBuilder.exitNode();
426         exitLog("typedef", removeNodeFromPath());
427     }
428
429     @Override
430     public void enterType_stmt(final YangParser.Type_stmtContext ctx) {
431         final int line = ctx.getStart().getLine();
432         final String typeName = stringFromNode(ctx);
433         enterLog("type", typeName, line);
434
435         final QName typeQName = parseQName(typeName);
436
437         TypeDefinition<?> type;
438         Type_body_stmtsContext typeBody = null;
439         for (int i = 0; i < ctx.getChildCount(); i++) {
440             if (ctx.getChild(i) instanceof Type_body_stmtsContext) {
441                 typeBody = (Type_body_stmtsContext) ctx.getChild(i);
442                 break;
443             }
444         }
445
446         // if this is base yang type...
447         if (BaseTypes.isYangBuildInType(typeName)) {
448             if (typeBody == null) {
449                 // check for types which must have body
450                 checkMissingBody(typeName, moduleName, line);
451                 // if there are no constraints, just grab default base yang type
452                 type = BaseTypes.defaultBaseTypeFor(typeName).orNull();
453                 addNodeToPath(type.getQName());
454                 moduleBuilder.setType(type);
455             } else {
456                 QName qname;
457                 switch (typeName) {
458                 case "union":
459                     qname = BaseTypes.UNION_QNAME;
460                     addNodeToPath(qname);
461                     UnionTypeBuilder unionBuilder = moduleBuilder.addUnionType(line, namespace, revision);
462                     Builder parent = moduleBuilder.getActualNode();
463                     unionBuilder.setParent(parent);
464                     moduleBuilder.enterNode(unionBuilder);
465                     break;
466                 case "identityref":
467                     qname = BaseTypes.IDENTITYREF_QNAME;
468                     addNodeToPath(qname);
469                     SchemaPath path = createActualSchemaPath(actualPath.peek());
470                     moduleBuilder.addIdentityrefType(line, path, getIdentityrefBase(typeBody));
471                     break;
472                 default:
473                     type = parseTypeWithBody(typeName, typeBody, createActualSchemaPath(actualPath.peek()), namespace, revision,
474                             yangModelPrefix, moduleBuilder.getActualNode());
475                     moduleBuilder.setType(type);
476                     addNodeToPath(type.getQName());
477                 }
478             }
479         } else {
480             type = parseUnknownTypeWithBody(typeQName, typeBody, createActualSchemaPath(actualPath.peek()), namespace, revision,
481                     yangModelPrefix, moduleBuilder.getActualNode());
482             // add parent node of this type statement to dirty nodes
483             moduleBuilder.markActualNodeDirty();
484             moduleBuilder.setType(type);
485             addNodeToPath(type.getQName());
486         }
487
488     }
489
490     private QName parseQName(final String typeName) {
491         final QName typeQName;
492         if (typeName.indexOf(':') != -1) {
493             final Iterator<String> split = COLON_SPLITTER.split(typeName).iterator();
494             final String prefix = split.next();
495             final String name = split.next();
496             if (prefix.equals(yangModelPrefix)) {
497                 typeQName = new QName(namespace, revision, prefix, name);
498             } else {
499                 typeQName = new QName(null, null, prefix, name);
500             }
501         } else {
502             typeQName = new QName(namespace, revision, yangModelPrefix, typeName);
503         }
504         return typeQName;
505     }
506
507     @Override
508     public void exitType_stmt(final YangParser.Type_stmtContext ctx) {
509         final String typeName = stringFromNode(ctx);
510         if ("union".equals(typeName)) {
511             moduleBuilder.exitNode();
512         }
513         exitLog("type", removeNodeFromPath());
514     }
515
516     @Override
517     public void enterGrouping_stmt(final YangParser.Grouping_stmtContext ctx) {
518         final int line = ctx.getStart().getLine();
519         final String groupName = stringFromNode(ctx);
520         enterLog("grouping", groupName, line);
521         QName groupQName = new QName(namespace, revision, yangModelPrefix, groupName);
522         addNodeToPath(groupQName);
523         SchemaPath path = createActualSchemaPath(actualPath.peek());
524
525         GroupingBuilder builder = moduleBuilder.addGrouping(ctx.getStart().getLine(), groupQName, path);
526         parseSchemaNodeArgs(ctx, builder);
527
528         moduleBuilder.enterNode(builder);
529     }
530
531     @Override
532     public void exitGrouping_stmt(final YangParser.Grouping_stmtContext ctx) {
533         moduleBuilder.exitNode();
534         exitLog("grouping", removeNodeFromPath());
535     }
536
537     @Override
538     public void enterContainer_stmt(final Container_stmtContext ctx) {
539         final int line = ctx.getStart().getLine();
540         final String containerName = stringFromNode(ctx);
541         enterLog("container", containerName, line);
542
543         QName containerQName = new QName(namespace, revision, yangModelPrefix, containerName);
544         addNodeToPath(containerQName);
545         SchemaPath path = createActualSchemaPath(actualPath.peek());
546
547         ContainerSchemaNodeBuilder builder = moduleBuilder.addContainerNode(line, containerQName, path);
548         parseSchemaNodeArgs(ctx, builder);
549         parseConstraints(ctx, builder.getConstraints());
550         builder.setConfiguration(getConfig(ctx, builder, moduleName, line));
551
552         for (int i = 0; i < ctx.getChildCount(); ++i) {
553             final ParseTree childNode = ctx.getChild(i);
554             if (childNode instanceof Presence_stmtContext) {
555                 builder.setPresence(true);
556                 break;
557             }
558         }
559
560         moduleBuilder.enterNode(builder);
561     }
562
563     @Override
564     public void exitContainer_stmt(final Container_stmtContext ctx) {
565         moduleBuilder.exitNode();
566         exitLog("container", removeNodeFromPath());
567     }
568
569     @Override
570     public void enterLeaf_stmt(final Leaf_stmtContext ctx) {
571         final int line = ctx.getStart().getLine();
572         final String leafName = stringFromNode(ctx);
573         enterLog("leaf", leafName, line);
574
575         QName leafQName = new QName(namespace, revision, yangModelPrefix, leafName);
576         addNodeToPath(leafQName);
577         SchemaPath path = createActualSchemaPath(actualPath.peek());
578
579         LeafSchemaNodeBuilder builder = moduleBuilder.addLeafNode(line, leafQName, path);
580         parseSchemaNodeArgs(ctx, builder);
581         parseConstraints(ctx, builder.getConstraints());
582         builder.setConfiguration(getConfig(ctx, builder, moduleName, line));
583
584         String defaultStr = null;
585         String unitsStr = null;
586         for (int i = 0; i < ctx.getChildCount(); i++) {
587             ParseTree child = ctx.getChild(i);
588             if (child instanceof Default_stmtContext) {
589                 defaultStr = stringFromNode(child);
590             } else if (child instanceof Units_stmtContext) {
591                 unitsStr = stringFromNode(child);
592             }
593         }
594         builder.setDefaultStr(defaultStr);
595         builder.setUnits(unitsStr);
596
597         moduleBuilder.enterNode(builder);
598     }
599
600     @Override
601     public void exitLeaf_stmt(final YangParser.Leaf_stmtContext ctx) {
602         moduleBuilder.exitNode();
603         exitLog("leaf", removeNodeFromPath());
604     }
605
606     @Override
607     public void enterUses_stmt(final YangParser.Uses_stmtContext ctx) {
608         final int line = ctx.getStart().getLine();
609         final String groupingPathStr = stringFromNode(ctx);
610         enterLog("uses", groupingPathStr, line);
611
612         UsesNodeBuilder builder = moduleBuilder.addUsesNode(line, groupingPathStr);
613
614         moduleBuilder.enterNode(builder);
615     }
616
617     @Override
618     public void exitUses_stmt(final YangParser.Uses_stmtContext ctx) {
619         moduleBuilder.exitNode();
620         exitLog("uses");
621     }
622
623     @Override
624     public void enterUses_augment_stmt(final YangParser.Uses_augment_stmtContext ctx) {
625         actualPath.push(new Stack<QName>());
626         final int line = ctx.getStart().getLine();
627         final String augmentPath = stringFromNode(ctx);
628         enterLog(AUGMENT_STR, augmentPath, line);
629
630         AugmentationSchemaBuilder builder = moduleBuilder.addAugment(line, augmentPath, augmentOrder++);
631
632         for (int i = 0; i < ctx.getChildCount(); i++) {
633             ParseTree child = ctx.getChild(i);
634             if (child instanceof Description_stmtContext) {
635                 builder.setDescription(stringFromNode(child));
636             } else if (child instanceof Reference_stmtContext) {
637                 builder.setReference(stringFromNode(child));
638             } else if (child instanceof Status_stmtContext) {
639                 builder.setStatus(parseStatus((Status_stmtContext) child));
640             } else if (child instanceof When_stmtContext) {
641                 builder.addWhenCondition(stringFromNode(child));
642             }
643         }
644
645         moduleBuilder.enterNode(builder);
646     }
647
648     @Override
649     public void exitUses_augment_stmt(final YangParser.Uses_augment_stmtContext ctx) {
650         moduleBuilder.exitNode();
651         exitLog(AUGMENT_STR);
652         actualPath.pop();
653     }
654
655     @Override
656     public void enterRefine_stmt(final YangParser.Refine_stmtContext ctx) {
657         final String refineString = stringFromNode(ctx);
658         enterLog("refine", refineString, ctx.getStart().getLine());
659
660         RefineHolderImpl refine = parseRefine(ctx, moduleName);
661         moduleBuilder.addRefine(refine);
662         moduleBuilder.enterNode(refine);
663     }
664
665     @Override
666     public void exitRefine_stmt(final YangParser.Refine_stmtContext ctx) {
667         moduleBuilder.exitNode();
668         exitLog("refine");
669     }
670
671     @Override
672     public void enterLeaf_list_stmt(final Leaf_list_stmtContext ctx) {
673         final int line = ctx.getStart().getLine();
674         final String leafListName = stringFromNode(ctx);
675         enterLog("leaf-list", leafListName, line);
676         QName leafListQName = new QName(namespace, revision, yangModelPrefix, leafListName);
677         addNodeToPath(leafListQName);
678         SchemaPath path = createActualSchemaPath(actualPath.peek());
679
680         LeafListSchemaNodeBuilder builder = moduleBuilder.addLeafListNode(line, leafListQName, path);
681         moduleBuilder.enterNode(builder);
682
683         parseSchemaNodeArgs(ctx, builder);
684         parseConstraints(ctx, builder.getConstraints());
685         builder.setConfiguration(getConfig(ctx, builder, moduleName, ctx.getStart().getLine()));
686
687         for (int i = 0; i < ctx.getChildCount(); ++i) {
688             final ParseTree childNode = ctx.getChild(i);
689             if (childNode instanceof Ordered_by_stmtContext) {
690                 final Ordered_by_stmtContext orderedBy = (Ordered_by_stmtContext) childNode;
691                 final boolean userOrdered = parseUserOrdered(orderedBy);
692                 builder.setUserOrdered(userOrdered);
693                 break;
694             }
695         }
696     }
697
698     @Override
699     public void exitLeaf_list_stmt(final YangParser.Leaf_list_stmtContext ctx) {
700         moduleBuilder.exitNode();
701         exitLog("leaf-list", removeNodeFromPath());
702     }
703
704     @Override
705     public void enterList_stmt(final List_stmtContext ctx) {
706         final int line = ctx.getStart().getLine();
707         final String listName = stringFromNode(ctx);
708         enterLog("list", listName, line);
709
710         QName listQName = new QName(namespace, revision, yangModelPrefix, listName);
711         addNodeToPath(listQName);
712         SchemaPath path = createActualSchemaPath(actualPath.peek());
713
714         ListSchemaNodeBuilder builder = moduleBuilder.addListNode(line, listQName, path);
715         moduleBuilder.enterNode(builder);
716
717         parseSchemaNodeArgs(ctx, builder);
718         parseConstraints(ctx, builder.getConstraints());
719         builder.setConfiguration(getConfig(ctx, builder, moduleName, line));
720
721         for (int i = 0; i < ctx.getChildCount(); ++i) {
722             ParseTree childNode = ctx.getChild(i);
723             if (childNode instanceof Ordered_by_stmtContext) {
724                 final Ordered_by_stmtContext orderedBy = (Ordered_by_stmtContext) childNode;
725                 final boolean userOrdered = parseUserOrdered(orderedBy);
726                 builder.setUserOrdered(userOrdered);
727             } else if (childNode instanceof Key_stmtContext) {
728                 List<String> key = createListKey((Key_stmtContext) childNode);
729                 builder.setKeys(key);
730             }
731         }
732     }
733
734     @Override
735     public void exitList_stmt(final List_stmtContext ctx) {
736         moduleBuilder.exitNode();
737         exitLog("list", removeNodeFromPath());
738     }
739
740     @Override
741     public void enterAnyxml_stmt(final YangParser.Anyxml_stmtContext ctx) {
742         final int line = ctx.getStart().getLine();
743         final String anyXmlName = stringFromNode(ctx);
744         enterLog("anyxml", anyXmlName, line);
745
746         QName anyXmlQName = new QName(namespace, revision, yangModelPrefix, anyXmlName);
747         addNodeToPath(anyXmlQName);
748         SchemaPath path = createActualSchemaPath(actualPath.peek());
749
750         AnyXmlBuilder builder = moduleBuilder.addAnyXml(line, anyXmlQName, path);
751         moduleBuilder.enterNode(builder);
752
753         parseSchemaNodeArgs(ctx, builder);
754         parseConstraints(ctx, builder.getConstraints());
755         builder.setConfiguration(getConfig(ctx, builder, moduleName, line));
756     }
757
758     @Override
759     public void exitAnyxml_stmt(final YangParser.Anyxml_stmtContext ctx) {
760         moduleBuilder.exitNode();
761         exitLog("anyxml", removeNodeFromPath());
762     }
763
764     @Override
765     public void enterChoice_stmt(final YangParser.Choice_stmtContext ctx) {
766         final int line = ctx.getStart().getLine();
767         final String choiceName = stringFromNode(ctx);
768         enterLog("choice", choiceName, line);
769
770         QName choiceQName = new QName(namespace, revision, yangModelPrefix, choiceName);
771         addNodeToPath(choiceQName);
772         SchemaPath path = createActualSchemaPath(actualPath.peek());
773
774         ChoiceBuilder builder = moduleBuilder.addChoice(line, choiceQName, path);
775         moduleBuilder.enterNode(builder);
776
777         parseSchemaNodeArgs(ctx, builder);
778         parseConstraints(ctx, builder.getConstraints());
779         builder.setConfiguration(getConfig(ctx, builder, moduleName, line));
780
781         // set 'default' case
782         for (int i = 0; i < ctx.getChildCount(); i++) {
783             ParseTree child = ctx.getChild(i);
784             if (child instanceof Default_stmtContext) {
785                 String defaultCase = stringFromNode(child);
786                 builder.setDefaultCase(defaultCase);
787                 break;
788             }
789         }
790     }
791
792     @Override
793     public void exitChoice_stmt(final YangParser.Choice_stmtContext ctx) {
794         moduleBuilder.exitNode();
795         exitLog("choice", removeNodeFromPath());
796     }
797
798     @Override
799     public void enterCase_stmt(final YangParser.Case_stmtContext ctx) {
800         final int line = ctx.getStart().getLine();
801         final String caseName = stringFromNode(ctx);
802         enterLog("case", caseName, line);
803
804         QName caseQName = new QName(namespace, revision, yangModelPrefix, caseName);
805         addNodeToPath(caseQName);
806         SchemaPath path = createActualSchemaPath(actualPath.peek());
807
808         ChoiceCaseBuilder builder = moduleBuilder.addCase(line, caseQName, path);
809         moduleBuilder.enterNode(builder);
810
811         parseSchemaNodeArgs(ctx, builder);
812         parseConstraints(ctx, builder.getConstraints());
813     }
814
815     @Override
816     public void exitCase_stmt(final YangParser.Case_stmtContext ctx) {
817         moduleBuilder.exitNode();
818         exitLog("case", removeNodeFromPath());
819     }
820
821     @Override
822     public void enterNotification_stmt(final YangParser.Notification_stmtContext ctx) {
823         final int line = ctx.getStart().getLine();
824         final String notificationName = stringFromNode(ctx);
825         enterLog("notification", notificationName, line);
826
827         QName notificationQName = new QName(namespace, revision, yangModelPrefix, notificationName);
828         addNodeToPath(notificationQName);
829         SchemaPath path = createActualSchemaPath(actualPath.peek());
830
831         NotificationBuilder builder = moduleBuilder.addNotification(line, notificationQName, path);
832         moduleBuilder.enterNode(builder);
833
834         parseSchemaNodeArgs(ctx, builder);
835     }
836
837     @Override
838     public void exitNotification_stmt(final YangParser.Notification_stmtContext ctx) {
839         moduleBuilder.exitNode();
840         exitLog("notification", removeNodeFromPath());
841     }
842
843     // Unknown nodes
844     @Override
845     public void enterIdentifier_stmt(final YangParser.Identifier_stmtContext ctx) {
846         handleUnknownNode(ctx.getStart().getLine(), ctx);
847     }
848
849     @Override
850     public void exitIdentifier_stmt(final YangParser.Identifier_stmtContext ctx) {
851         moduleBuilder.exitNode();
852         exitLog("unknown-node", removeNodeFromPath());
853     }
854
855     @Override public void enterUnknown_statement(final YangParser.Unknown_statementContext ctx) {
856         handleUnknownNode(ctx.getStart().getLine(), ctx);
857     }
858
859     @Override public void exitUnknown_statement(final YangParser.Unknown_statementContext ctx) {
860         moduleBuilder.exitNode();
861         exitLog("unknown-node", removeNodeFromPath());
862     }
863
864     @Override public void enterUnknown_statement2(final YangParser.Unknown_statement2Context ctx) {
865         handleUnknownNode(ctx.getStart().getLine(), ctx);
866     }
867
868     @Override public void exitUnknown_statement2(final YangParser.Unknown_statement2Context ctx) {
869         moduleBuilder.exitNode();
870         exitLog("unknown-node", removeNodeFromPath());
871     }
872
873     @Override public void enterUnknown_statement3(final YangParser.Unknown_statement3Context ctx) {
874         handleUnknownNode(ctx.getStart().getLine(), ctx);
875     }
876
877     @Override public void exitUnknown_statement3(final YangParser.Unknown_statement3Context ctx) {
878         moduleBuilder.exitNode();
879         exitLog("unknown-node", removeNodeFromPath());
880     }
881
882     @Override
883     public void enterRpc_stmt(final YangParser.Rpc_stmtContext ctx) {
884         final int line = ctx.getStart().getLine();
885         final String rpcName = stringFromNode(ctx);
886         enterLog("rpc", rpcName, line);
887
888         QName rpcQName = new QName(namespace, revision, yangModelPrefix, rpcName);
889         addNodeToPath(rpcQName);
890         SchemaPath path = createActualSchemaPath(actualPath.peek());
891
892         RpcDefinitionBuilder rpcBuilder = moduleBuilder.addRpc(line, rpcQName, path);
893         moduleBuilder.enterNode(rpcBuilder);
894
895
896         parseSchemaNodeArgs(ctx, rpcBuilder);
897     }
898
899     @Override
900     public void exitRpc_stmt(final YangParser.Rpc_stmtContext ctx) {
901         moduleBuilder.exitNode();
902         exitLog("rpc", removeNodeFromPath());
903     }
904
905     @Override
906     public void enterInput_stmt(final YangParser.Input_stmtContext ctx) {
907         final int line = ctx.getStart().getLine();
908         final String input = "input";
909         enterLog(input, input, line);
910
911         QName rpcQName = new QName(namespace, revision, yangModelPrefix, input);
912         addNodeToPath(rpcQName);
913         SchemaPath path = createActualSchemaPath(actualPath.peek());
914
915         ContainerSchemaNodeBuilder builder = moduleBuilder.addRpcInput(line, rpcQName, path);
916         moduleBuilder.enterNode(builder);
917         builder.setConfiguration(true);
918
919         parseSchemaNodeArgs(ctx, builder);
920         parseConstraints(ctx, builder.getConstraints());
921     }
922
923     @Override
924     public void exitInput_stmt(final YangParser.Input_stmtContext ctx) {
925         moduleBuilder.exitNode();
926         exitLog("input", removeNodeFromPath());
927     }
928
929     @Override
930     public void enterOutput_stmt(final YangParser.Output_stmtContext ctx) {
931         final int line = ctx.getStart().getLine();
932         final String output = "output";
933         enterLog(output, output, line);
934
935         QName rpcQName = new QName(namespace, revision, yangModelPrefix, output);
936         addNodeToPath(rpcQName);
937         SchemaPath path = createActualSchemaPath(actualPath.peek());
938
939         ContainerSchemaNodeBuilder builder = moduleBuilder.addRpcOutput(path, rpcQName, line);
940         moduleBuilder.enterNode(builder);
941         builder.setConfiguration(true);
942
943         parseSchemaNodeArgs(ctx, builder);
944         parseConstraints(ctx, builder.getConstraints());
945     }
946
947     @Override
948     public void exitOutput_stmt(final YangParser.Output_stmtContext ctx) {
949         moduleBuilder.exitNode();
950         exitLog("output", removeNodeFromPath());
951     }
952
953     @Override
954     public void enterFeature_stmt(final YangParser.Feature_stmtContext ctx) {
955         final int line = ctx.getStart().getLine();
956         final String featureName = stringFromNode(ctx);
957         enterLog("feature", featureName, line);
958
959         QName featureQName = new QName(namespace, revision, yangModelPrefix, featureName);
960         addNodeToPath(featureQName);
961         SchemaPath path = createActualSchemaPath(actualPath.peek());
962
963         FeatureBuilder featureBuilder = moduleBuilder.addFeature(line, featureQName, path);
964         moduleBuilder.enterNode(featureBuilder);
965
966         parseSchemaNodeArgs(ctx, featureBuilder);
967     }
968
969     @Override
970     public void exitFeature_stmt(final YangParser.Feature_stmtContext ctx) {
971         moduleBuilder.exitNode();
972         exitLog("feature", removeNodeFromPath());
973     }
974
975     @Override
976     public void enterDeviation_stmt(final YangParser.Deviation_stmtContext ctx) {
977         final int line = ctx.getStart().getLine();
978         final String targetPath = stringFromNode(ctx);
979         enterLog("deviation", targetPath, line);
980
981         String reference = null;
982         String deviate = null;
983         DeviationBuilder builder = moduleBuilder.addDeviation(line, targetPath);
984         moduleBuilder.enterNode(builder);
985
986         for (int i = 0; i < ctx.getChildCount(); i++) {
987             ParseTree child = ctx.getChild(i);
988             if (child instanceof Reference_stmtContext) {
989                 reference = stringFromNode(child);
990             } else if (child instanceof Deviate_not_supported_stmtContext) {
991                 deviate = stringFromNode(child);
992             } else if (child instanceof Deviate_add_stmtContext) {
993                 deviate = stringFromNode(child);
994             } else if (child instanceof Deviate_replace_stmtContext) {
995                 deviate = stringFromNode(child);
996             } else if (child instanceof Deviate_delete_stmtContext) {
997                 deviate = stringFromNode(child);
998             }
999         }
1000         builder.setReference(reference);
1001         builder.setDeviate(deviate);
1002     }
1003
1004     @Override
1005     public void exitDeviation_stmt(final YangParser.Deviation_stmtContext ctx) {
1006         moduleBuilder.exitNode();
1007         exitLog("deviation");
1008     }
1009
1010     @Override
1011     public void enterIdentity_stmt(final YangParser.Identity_stmtContext ctx) {
1012         final int line = ctx.getStart().getLine();
1013         final String identityName = stringFromNode(ctx);
1014         enterLog("identity", identityName, line);
1015
1016         final QName identityQName = new QName(namespace, revision, yangModelPrefix, identityName);
1017         addNodeToPath(identityQName);
1018         SchemaPath path = createActualSchemaPath(actualPath.peek());
1019
1020         IdentitySchemaNodeBuilder builder = moduleBuilder.addIdentity(identityQName, line, path);
1021         moduleBuilder.enterNode(builder);
1022
1023
1024         parseSchemaNodeArgs(ctx, builder);
1025
1026         for (int i = 0; i < ctx.getChildCount(); i++) {
1027             ParseTree child = ctx.getChild(i);
1028             if (child instanceof Base_stmtContext) {
1029                 String baseIdentityName = stringFromNode(child);
1030                 builder.setBaseIdentityName(baseIdentityName);
1031             }
1032         }
1033     }
1034
1035     @Override
1036     public void exitIdentity_stmt(final YangParser.Identity_stmtContext ctx) {
1037         moduleBuilder.exitNode();
1038         exitLog("identity", removeNodeFromPath());
1039     }
1040
1041     public ModuleBuilder getModuleBuilder() {
1042         return moduleBuilder;
1043     }
1044
1045     private void enterLog(final String p1, final String p2, final int line) {
1046         LOGGER.trace("entering {} {} ({})", p1, p2, line);
1047     }
1048
1049     private void exitLog(final String p1) {
1050         LOGGER.trace("exiting {}", p1);
1051     }
1052
1053     private void exitLog(final String p1, final QName p2) {
1054         LOGGER.trace("exiting {} {}", p1, p2.getLocalName());
1055     }
1056
1057     private void setLog(final String p1, final String p2) {
1058         LOGGER.trace("setting {} {}", p1, p2);
1059     }
1060
1061     private void handleUnknownNode(final int line, final ParseTree ctx) {
1062         final String nodeParameter = stringFromNode(ctx);
1063         enterLog("unknown-node", nodeParameter, line);
1064
1065         final String nodeTypeStr = ctx.getChild(0).getText();
1066         final Iterator<String> splittedElement = COLON_SPLITTER.split(nodeTypeStr).iterator();
1067         final String e0 = splittedElement.next();
1068         final QName nodeType;
1069         if (splittedElement.hasNext()) {
1070             nodeType = new QName(namespace, revision, e0, splittedElement.next());
1071         } else {
1072             nodeType = new QName(namespace, revision, yangModelPrefix, e0);
1073         }
1074
1075         QName qname;
1076         try {
1077             if (!Strings.isNullOrEmpty(nodeParameter)) {
1078                 final Iterable<String> splittedName = COLON_SPLITTER.split(nodeParameter);
1079                 final Iterator<String> it = splittedName.iterator();
1080
1081                 if (Iterables.size(splittedName) == 2) {
1082                     qname = new QName(null, null, it.next(), it.next());
1083                 } else {
1084                     qname = new QName(namespace, revision, yangModelPrefix, it.next());
1085                 }
1086             } else {
1087                 qname = nodeType;
1088             }
1089         } catch (IllegalArgumentException e) {
1090             qname = nodeType;
1091         }
1092         addNodeToPath(qname);
1093         SchemaPath path = createActualSchemaPath(actualPath.peek());
1094
1095         UnknownSchemaNodeBuilderImpl builder = moduleBuilder.addUnknownSchemaNode(line, qname, path);
1096         builder.setNodeType(nodeType);
1097         builder.setNodeParameter(nodeParameter);
1098
1099
1100         parseSchemaNodeArgs(ctx, builder);
1101         moduleBuilder.enterNode(builder);
1102     }
1103
1104 }