001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdo.query;
012:
013: import com.versant.core.common.Debug;
014: import com.versant.core.common.CmdBitSet;
015: import com.versant.core.metadata.ClassMetaData;
016: import com.versant.core.metadata.FieldMetaData;
017: import com.versant.core.metadata.MDStatics;
018:
019: import com.versant.core.common.BindingSupportImpl;
020:
021: /**
022: * This node is created when a field that is a reference to a PC class is
023: * navigated in an expression. Information required to generate a subquery
024: * or join is included here. The child nodes become extra expressions for
025: * the where clause.
026: */
027: public class FieldNavNode extends UnaryNode {
028:
029: public String lexeme;
030: /**
031: * If we were prefixed with a class cast expression then this is the
032: * type being cast to.
033: */
034: public String cast;
035: /**
036: * The field being navigated (normal ref or polyref). This is used to
037: * construct a join from the column(s) for this field in the src table to
038: * the primary key of the fields class.
039: */
040: public FieldMetaData fmd;
041: /**
042: * The class being referenced.
043: */
044: public ClassMetaData targetClass;
045:
046: public boolean resolved;
047: /**
048: * Does this node represent an embedded field.
049: */
050: public boolean embedded;
051:
052: /**
053: * The variable being accessed. This is used to locate the query for
054: * the variable.
055: */
056: public VarNode var;
057:
058: public FieldNavNode() {
059: }
060:
061: public Object accept(NodeVisitor visitor, Object[] results) {
062: return visitor.visitFieldNavNode(this , results);
063: }
064:
065: public String toString() {
066: return super .toString() + " "
067: + (cast != null ? "(" + cast + ")" : "")
068: + (fmd != null ? fmd.toString() : lexeme) + " as "
069: + asValue;
070: }
071:
072: protected void normalizeImp() {
073: if (embedded) {
074: throw BindingSupportImpl.getInstance().internal(
075: "This node should be dissolved");
076: }
077: super .normalizeImp();
078: }
079:
080: /**
081: * Resolve field refs and so on relative to the compiler. This must
082: * recursively resolve any child nodes.
083: */
084: public void resolve(QueryParser comp, ClassMetaData cmd,
085: boolean ordering) {
086: if (Debug.DEBUG)
087: System.out.println("### FieldNavNode.resolve " + this );
088: if (asValue != null)
089: childList.asValue = asValue;
090: ClassMetaData tcmd = null;
091: if (!resolved && !(parent instanceof FieldNavNode)) {
092: // this could be a variable reference
093: var = comp.findVar(lexeme);
094: if (var != null) {
095: tcmd = resolveVariable();
096: if (cast != null) {
097: ClassMetaData c = comp.resolveCastType(cast)[0];
098: if (tcmd == c) {
099: cast = null; // redundant cast
100: } else {
101: tcmd = c;
102: }
103: }
104: }
105: }
106: if (var == null)
107: tcmd = resolveField(cmd, comp);
108: if (childList != null) {
109: childList.resolve(comp, tcmd, false);
110: }
111: if (embedded) {
112: parent.replaceChild(this , childList);
113: }
114: resolved = true;
115: }
116:
117: private ClassMetaData resolveField(ClassMetaData cmd,
118: QueryParser comp) {
119: ClassMetaData tcmd;
120:
121: if (parent instanceof FieldNavNode) {
122: FieldNavNode pFnn = (FieldNavNode) parent;
123: if (pFnn.embedded) {
124: String fname = pFnn.fmd.name + "/" + lexeme;
125: FieldMetaData[] fmds = pFnn.fmd.classMetaData.fields;
126: for (int i = 0; i < fmds.length; i++) {
127: FieldMetaData fieldMetaData = fmds[i];
128: if (fieldMetaData.name.equals(fname)) {
129: fmd = this .fmd = fieldMetaData;
130: break;
131: }
132: }
133: }
134: }
135:
136: if (fmd == null) {
137: fmd = cmd.getFieldMetaData(lexeme);
138: }
139: if (fmd == null) {
140: throw BindingSupportImpl.getInstance().runtime(
141: "Field '" + lexeme + "' not found on " + cmd.qname);
142: }
143: embedded = fmd.embedded;
144:
145: if (fmd.category != MDStatics.CATEGORY_REF
146: && fmd.category != MDStatics.CATEGORY_POLYREF) {
147: throw BindingSupportImpl
148: .getInstance()
149: .runtime(
150: "Field '"
151: + lexeme
152: + "' on "
153: + cmd.qname
154: + " is not a reference to another PC class");
155: }
156: tcmd = fmd.typeMetaData;
157: if (cast != null) {
158: ClassMetaData c = comp.resolveCastType(cast)[0];
159: if (tcmd == c) {
160: cast = null; // redundant cast
161: } else {
162: tcmd = c;
163: }
164: } else if (tcmd == null) { // polyref
165: throw BindingSupportImpl
166: .getInstance()
167: .runtime(
168: "Field '"
169: + fmd.getTypeQName()
170: + "' "
171: + " must be cast to a persistent class to be "
172: + "navigated in a query");
173: }
174:
175: targetClass = tcmd;
176:
177: if (embedded)
178: return cmd;
179: return tcmd;
180: }
181:
182: private ClassMetaData resolveVariable() {
183: ClassMetaData vcmd = var.getCmd();
184: if (vcmd == null) {
185: throw BindingSupportImpl.getInstance().runtime(
186: "Variable '" + lexeme
187: + "' is not of a persistent class");
188: }
189: if (!var.bound)
190: var.insertVarBindingNode(parent);
191:
192: return vcmd;
193: }
194:
195: public Field visit(MemVisitor visitor, Object obj) {
196: return visitor.visitFieldNavNode(this , obj);
197: }
198:
199: /**
200: * Return the FieldMetaData at the end of this chain.
201: */
202: public FieldMetaData getResultFmd() {
203: if (childList instanceof FieldNavNode) {
204: return ((FieldNavNode) childList).getResultFmd();
205: } else if (childList instanceof FieldNode) {
206: return ((FieldNode) childList).fmd;
207: } else {
208: throw BindingSupportImpl.getInstance().runtime("");
209: }
210: }
211:
212: /**
213: * Implement this in nodes to udpate the ClassMetaData depency of the graph.
214: * This is used for query eviction.
215: *
216: * @param bitSet
217: */
218: public void updateEvictionDependency(CmdBitSet bitSet) {
219: if (targetClass != null)
220: bitSet.addPlus(targetClass);
221: }
222:
223: public Object arrive(NodeVisitor v, Object msg) {
224: return v.arriveFieldNavNode(this, msg);
225: }
226:
227: }
|