2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.schema2beans;
48
* The DDRegistryParser is a parser/Iterator on a set of graphs, as
49
* registered in the schema2beans registry (DDRegistry).
51
* DDParser is a parser/Iterator on a single schema2beans graph, using
52
* a schema2beans path description to define what should be parsed.
53
* DDRegistryParser extend the functiionality of DDParser by providing
54
* a parsing mechanism on a set of graphs (instead of a single one) and
55
* by adding more syntax to the DDParser schema2beans path syntax.
57
* Where DDParser defined a DDLocation to define a location reference in
58
* the parsed graph, DDRegistryParser defines a DDCursor. The DDCursor
59
* defines a location on a set of graphs (a DDCursor might have a parent
60
* root defined in another graph).
62
* The DDRegistryParser instances are created by the DDRegistry.
64
public class DDRegistryParser implements Iterator {
66
static final String CURRENT_CURSOR = "."; // NOI18N
69
* Analyze and resolve the vriable references specified in the path
71
static public class PathResolver {
73
static final char VARBEGIN = '{';
74
static final char VAREND = '}';
75
static final char VALUE = '#';
77
// Current module (display/non unique) name
78
static final String VAR_MODNAME = "mname"; // NOI18N
80
// Current module unique name
81
static final String VAR_UNAME = "uname"; // NOI18N
83
// Parent schema2beans type of the specified type {#ptype.EnvEntry}
84
// would be either Session or Entity.
85
static final String VAR_PTYPE = "ptype"; // NOI18N
87
static final String VAR_TYPE = "type"; // NOI18N
93
public PathResolver() {
97
public PathResolver(DDCursor cursor, String path) {
98
this.result = this.resolvePath(cursor, path);
102
static boolean needResolving(String path) {
104
return (path.indexOf(VARBEGIN) != -1);
110
* This resolve all the variables referenced in the path string
111
* using the knowledge of its current location. A variable is
112
* defined with braces {}, and might use any of the values:
114
* #mname, #uname, #ptype, #type
116
* or any reference to a property of the current graph:
118
* {NodeName} // no # in this case
121
String resolvePath(DDCursor cur, String path) {
122
if(path.indexOf(VARBEGIN) != -1) {
123
int i1 = path.indexOf(VARBEGIN);
124
int i2 = path.indexOf(VAREND);
126
(String)this.resolvePathVar(cur, path.substring(i1+1, i2));
127
return path.substring(0, i1).trim() + v +
128
this.resolvePath(cur, path.substring(i2+1));
133
Object resolvePathVar(DDCursor cur, String path) {
134
//path = path.trim();
136
if (path.indexOf(VARBEGIN) != -1 || path.indexOf(VAREND) != -1) {
137
throw new IllegalArgumentException(Common.getMessage(
138
"CannotNestDeclaration_msg"));
141
if (path.indexOf('#') == 0 ) {
142
path = path.substring(1, path.length());
144
String remSuffix = null;
145
int idx = path.indexOf('-');
147
// Might have to remove a suffix from the value
148
remSuffix = path.substring(idx+1);
149
path = path.substring(0, idx);
152
if (path.startsWith(VAR_MODNAME)) {
153
int in = path.indexOf(':');
155
String name = path.substring(in+1);
156
path = this.getDDNameValue(cur, name).toString();;
157
path = cur.getRegistry().getName(path);
159
path = cur.getRegistry().getName(cur);
162
if (path.startsWith(VAR_UNAME)) {
163
path = cur.getRegistry().getID(cur);
165
if (path.startsWith(VAR_PTYPE)) {
166
int i = path.indexOf('.');
168
String t = path.substring(i+1);
172
bean = this.getBean(pc.getRoot(), t);
176
} while(bean == null && pc != null);
179
path = bean.parent().name();
183
if (path.startsWith(VAR_TYPE)) {
184
path = cur.getRoot().name();
187
if (remSuffix != null) {
188
if (path.endsWith(remSuffix)) {
189
path = path.substring(0, path.length() -
196
return this.getDDNameValue(cur, path);
200
private Object getDDNameValue(DDCursor pc, String path) {
203
// Look for the value in the DDCursors and current graph
204
// hierarchy (look first in the graph then in other DDCursors)
206
val = this.getValue(pc.getRoot(), path);
210
} while(val == null && pc != null);
214
BaseBean getBean(BaseBean root, String name) {
215
while (root != null && !root.isRoot()) {
216
if (root.hasName(name))
218
root = root.parent();
223
String getValue(BaseBean root, String name) {
228
val = (String)root.getValue(name);
230
} catch(Exception e) {
231
// Unknown property name - ignore it
233
root = root.parent();
234
} while (root != null && !root.isRoot());
239
public String toString() {
245
* DDCursor is a location reference in one of the DDRegistry graphs.
246
* Note that DDCursor can be created in two different ways: from a schema2beans
247
* path or from a schema2beans node (BaseBean).
249
static public class DDCursor {
254
public DDCursor(DDCursor parent, String path) {
255
this(parent, (BaseBean)null);
259
public DDCursor(DDCursor parent, BaseBean root) {
260
this.parent = parent;
262
if (this.registry == null && parent != null)
263
this.registry = parent.registry;
266
public DDCursor(DDRegistry reg, String path) {
273
public DDCursor(DDRegistry reg, BaseBean root) {
279
public DDRegistry getRegistry() {
280
return this.registry;
283
public BaseBean getRoot() {
287
public DDCursor getParent() {
291
public Object getValue(String name) {
293
return this.root.getValue(name);
298
void resolve(String path) {
300
if (path == null) return;
302
if (path.equals("")) return; // NOI18N
304
if (path.startsWith("[") && path.endsWith("]")) { // NOI18N
305
this.resolveGraph(path.substring(1,path.length()-1));
309
// Find the proper root
310
if (this.parent == null) {
311
throw new IllegalStateException(Common.getMessage(
312
"CantResolveBecauseMissingParent_msg", path));
315
// Resolve any embeded {} variables
316
if (PathResolver.needResolving(path))
317
path = (new PathResolver(this.parent, path)).toString();
319
BaseBean root = this.parent.getRoot();
322
DDParser p = new DDParser(root, path);
325
if (o instanceof BaseBean) {
326
this.root = (BaseBean)o;
328
throw new IllegalStateException(
330
"ParsingPathDoesntResolveToGraphNodeElement_msg",
331
path, o.getClass().getName(), o.toString()));
334
throw new IllegalStateException(Common.getMessage(
335
"NoElementFoundPath_msg", path));
338
throw new IllegalStateException(Common.getMessage(
339
"NoRootFoundForPath_msg", path));
343
void resolveGraph(String path) {
344
String pathRoot = null;
346
if (PathResolver.needResolving(path))
347
path = (new PathResolver(this.parent, path)).toString();
349
int idx = path.indexOf(':');
351
pathRoot = path.substring(idx+1);
352
path = path.substring(0, idx);
355
BaseBean[] beans = this.registry.getRoots(path);
357
if (beans.length > 0) {
358
this.root = beans[0];
359
if (pathRoot != null) {
360
DDCursor cur = new DDRegistryParser.DDCursor(this,
362
this.root = cur.getRoot();
367
public String toString() {
370
if (this.parent != null)
371
p = this.parent.toString();
375
if (this.root != null)
379
return "Parent:"+p+" Root:"+r; // NOI18N
382
public String dump() {
383
if (this.root != null)
384
return this.root.dumpBeanNode();
386
return "<null graph>"; // NOI18N
394
// The root of the parsing can be defined by:
397
// - a graph reference in the scope definition: [graph_name]
399
DDRegistryParser parentParser = null;
400
DDCursor parentCursor = null;
401
DDRegistryParser parserRoot = null;
403
ParserSet parser = null;
405
public DDRegistryParser(DDRegistry reg, DDRegistryParser rp,
408
this.initialize(path, rp, null);
411
public DDRegistryParser(DDRegistry reg, DDCursor cursor,
414
this.initialize(path, null, cursor);
417
public DDRegistryParser(DDRegistry reg, String path) {
419
this.initialize(path, null, null);
422
public DDRegistry getRegistry() {
423
return this.registry;
427
* Initialize the parser. Either the parser or cursor is set, not both.
429
void initialize(String path, DDRegistryParser regParser, DDCursor cursor) {
430
String graphName = null;
431
String subpath = null;
432
String parsingPath = null;
435
// A scope ([NAME]) refers to a graph or set of graphs in the
436
// registry. In such case, the scope is the root of the parser
437
// (and parserRoot = null) since we'll get our root beans from the
438
// scope and not from the parent parser or cursor
439
// (if any specified).
441
//path = path.trim();
443
DDCursor cur = cursor;
444
if (cur == null && regParser != null)
445
cur = regParser.getCursor();
447
if (path.startsWith("[")) { // NOI18N
448
int idx = path.indexOf(']');
449
graphName = path.substring(1, idx);
451
if (path.length() > idx+1)
452
path = path.substring(idx+1, path.length());
454
path = "."; // NOI18N
456
idx = graphName.indexOf(':');
458
subpath = graphName.substring(idx+1, graphName.length()-1);
459
graphName = graphName.substring(0, idx);
461
if (PathResolver.needResolving(subpath))
462
subpath = (new PathResolver(cur, subpath)).toString();
465
if (PathResolver.needResolving(graphName))
466
graphName = (new PathResolver(cur, graphName)).toString();
468
if (graphName.equals(CURRENT_CURSOR) && cursor != null)
472
if (PathResolver.needResolving(path))
473
parsingPath = (new PathResolver(cur, path)).toString();
478
if (graphName == null && regParser == null && cursor == null) {
479
throw new IllegalStateException(Common.getMessage(
480
"CantFindRootForParser_msg"));
484
// We know that we have a parent root - if the graphName
485
// is specified, then we get the root from the registry
487
if (graphName != null) {
490
// The parser is initialized with an absolute graph
491
// name reference, such as [ejbmodule]
492
// That means that we get 1-n BaseBean(s) from the registry
493
// that we use as the root of the parsing.
495
BaseBean[] beans = this.registry.getRoots(graphName);
496
this.parser = new ParserSet(beans, null, parsingPath);
497
this.parser.setRoot();
498
} else if (regParser != null) {
501
// The parser is initialized from another parser. That means
502
// that the current position of the other parser is used
503
// as the root of this new parser. However, we need to consider
504
// two cases: 1. the other parser has defined a set of roots
505
// (previous case when the parser is initialized with []),
506
// 2. the other parser has only one root.
507
// If the other parser was initialized as a set of roots,
508
// we need to get all of them to initialize this parser (or
509
// we'll miss the 2-n graphs in the parsing).
511
if (regParser.isRoot() && regParser.getRoots().length > 1) {
512
BaseBean[] beans = regParser.getRoots();
514
// If the other parser has a parsingPath, we need to
515
// get the beans using this parsingPath
516
if (regParser.hasParsingPath()) {
517
String pp = regParser.getParsingPath();
518
ArrayList tmpArr = new ArrayList();
520
for (int i=0; i<beans.length; i++) {
521
DDParser tmp = new DDParser(beans[i], pp);
523
tmpArr.add(tmp.next());
525
BaseBean[] newBeans = new BaseBean[tmpArr.size()];
526
beans = (BaseBean[])tmpArr.toArray(newBeans);
529
this.parser = new ParserSet(beans, null, parsingPath);
531
while (regParser.current() == null && regParser.hasNext())
534
this.parser = new ParserSet((BaseBean)regParser.current(), cur,
539
} else if (cursor != null) {
541
// The parser is initialized from a DDCursor position.
543
this.parser = new ParserSet(cursor.getRoot(), cur, parsingPath);
545
throw new IllegalStateException( Common.getMessage(
546
"NoParentSpecified_msg"));
552
return this.parser.isRoot();
555
boolean hasParsingPath() {
556
return this.parser.hasParsingPath();
559
String getParsingPath() {
560
return this.parser.getParsingPath();
563
BaseBean[] getRoots() {
564
return this.parser.getRoots();
567
// Reset the current ParsetSet to use the next available root from the
569
public Object next() {
570
return this.parser.next();
573
public boolean hasNext() {
574
return this.parser.hasNext();
577
public DDCursor getCursor() {
578
Object o = this.current();
580
if (o instanceof BaseBean) {
581
BaseBean b = (BaseBean)o;
582
if (b == null && this.hasNext())
583
b = (BaseBean)this.next();
585
return new DDCursor(this.registry, b);
587
// Return our parent cursor or build a new one
588
DDCursor cur = this.parser.getParentCursor();
590
BaseBean[] beans = this.parser.getRoots();
591
cur = new DDCursor(this.registry, beans[0]);
598
public DDParser.DDLocation getLocation() {
599
return this.parser.getLocation();
602
public Object current() {
603
return this.parser.current();
606
public void remove() {
607
throw new UnsupportedOperationException();
610
public Object getValue(String ddName) {
611
BaseBean b = (BaseBean)this.current();
612
if (b == null && this.hasNext())
613
b = (BaseBean)this.next();
616
// This will seach for the ddName element in the current
617
// graph and will also search in the ParentCursor graph
618
// if the element is not found
619
DDCursor cur = new DDCursor(this.parser.getParentCursor(), b);
620
PathResolver p = new PathResolver();
621
Object obj = p.resolvePathVar(cur, ddName);
630
* ParserSet handle the parsing of a set of graph through the usage
631
* of DDParser. DDRegistryParser delegates the multi-graph parsing to
635
private BaseBean[] roots;
637
private String parsingPath;
638
private DDParser curParser;
639
private boolean isRoot;
640
private DDCursor parentCursor;
642
ParserSet(BaseBean[] roots, DDCursor cur, String path) {
643
if (roots != null && roots.length > 0 && roots[0] != null) {
647
this.parsingPath = path;
648
this.parentCursor = cur;
649
this.adjustPathRoot();
652
throw new IllegalArgumentException(Common.getMessage(
653
"NoRootSpecified_msg", path));
657
ParserSet(BaseBean root, DDCursor cur, String path) {
658
this(new BaseBean[] {root}, cur, path);
661
void adjustPathRoot() {
662
if (this.parsingPath.startsWith("../")) { // NOI18N
663
int i = this.parsingPath.lastIndexOf("../"); // NOI18N
666
for (int j=0; j<this.roots.length; j++) {
667
if (this.roots[j].isRoot())
668
throw new Schema2BeansRuntimeException(Common.getMessage(
669
"CantAccessBaseBeanNode_msg", this.parsingPath));
670
this.roots[j] = this.roots[j].parent();
673
this.parsingPath = this.parsingPath.substring(i+3);
677
BaseBean[] getRoots() {
681
DDCursor getParentCursor() {
682
return this.parentCursor;
693
boolean hasParsingPath() {
694
if (this.parsingPath != null)
695
return !this.parsingPath.equals("."); // NOI18N
700
String getParsingPath() {
701
return this.parsingPath;
704
private boolean newParser() {
705
if (this.cur < this.roots.length) {
708
new DDParser(this.roots[this.cur++], this.parsingPath);
710
} catch(NoSuchElementException e) {
711
// If the element is not found, try with our parent root,
712
// this might be a linked graph.
713
if(this.parentCursor != null) {
716
new BaseBean[] {this.parentCursor.getRoot()};
717
this.parentCursor = this.parentCursor.getParent();
718
return this.newParser();
729
boolean more = this.curParser.hasNext();
731
while (!more && this.newParser())
732
more = this.curParser.hasNext();
737
DDParser.DDLocation getLocation() {
738
return this.curParser.getLocation();
741
// Return what the parser has currently
743
return this.curParser.current();
746
// Get the next element available from our set of parsers
748
if (this.hasNext()) {
749
return this.curParser.next();
751
throw new NoSuchElementException();