001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.sql.framework.model.impl;
042:
043: import java.util.ArrayList;
044: import java.util.List;
045:
046: import org.netbeans.modules.sql.framework.common.utils.TagParserUtility;
047: import org.netbeans.modules.sql.framework.model.GUIInfo;
048: import org.netbeans.modules.sql.framework.model.SQLCondition;
049: import org.netbeans.modules.sql.framework.model.SQLConstants;
050: import org.netbeans.modules.sql.framework.model.SQLInputObject;
051: import org.netbeans.modules.sql.framework.model.SQLJoinOperator;
052: import org.netbeans.modules.sql.framework.model.SQLJoinTable;
053: import org.netbeans.modules.sql.framework.model.SQLModelObjectFactory;
054: import org.netbeans.modules.sql.framework.model.SQLObject;
055: import org.netbeans.modules.sql.framework.model.SourceTable;
056: import org.netbeans.modules.sql.framework.model.visitors.SQLVisitor;
057: import org.w3c.dom.Element;
058: import org.w3c.dom.Node;
059: import org.w3c.dom.NodeList;
060: import net.java.hulp.i18n.Logger;
061: import com.sun.sql.framework.exception.BaseException;
062: import org.netbeans.modules.etl.logger.Localizer;
063: import org.netbeans.modules.etl.logger.LogUtil;
064:
065: /**
066: * Defines joins on tables.
067: *
068: * @author Ritesh Adval, Sudhi Seshachala
069: * @version $Revision$
070: */
071: public class SQLJoinOperatorImpl extends SQLConnectableObjectImpl
072: implements SQLJoinOperator {
073:
074: private static final String LOG_CATEGORY = SQLJoinOperatorImpl.class
075: .getName();
076: private static transient final Logger mLogger = LogUtil
077: .getLogger(SQLJoinOperatorImpl.class.getName());
078: private static transient final Localizer mLoc = Localizer.get();
079: /** GUI state information */
080: private transient GUIInfo guiInfo = new GUIInfo();
081: private SQLCondition jCondition;
082: // ref to root join ie join whose one input is this join.
083: private SQLJoinOperator rootJoin;
084:
085: /** Creates a new default instance of SQLJoin */
086: public SQLJoinOperatorImpl() {
087: super ();
088:
089: type = SQLConstants.JOIN;
090: setJoinType(SQLConstants.INNER_JOIN);
091:
092: SQLInputObjectImpl inputObject = new SQLInputObjectImpl(LEFT,
093: LEFT, null);
094: inputMap.put(LEFT, inputObject);
095:
096: inputObject = new SQLInputObjectImpl(RIGHT, RIGHT, null);
097: inputMap.put(RIGHT, inputObject);
098:
099: this .jCondition = SQLModelObjectFactory.getInstance()
100: .createSQLCondition(JOIN_CONDITION);
101: this .jCondition.setParent(this );
102: // set condition to empty string, if it is not set then it is null
103: // but when we get a SQLCondition from condition builder this is set to empty
104: // string though user has not modified it so to make sure equal method works
105: // properly we need to set it to empty string
106: this .jCondition.setConditionText("");
107: }
108:
109: /**
110: * Creates a new instance of SQLJoinOperatorImpl using information from the given
111: * SQLJoinOperator object.
112: *
113: * @param src SQLJoinOperator from which to obtain attributes, etc., for this new
114: * instance
115: * @throws BaseException if error occurs during instantiation
116: */
117: public SQLJoinOperatorImpl(SQLJoinOperator src)
118: throws BaseException {
119: this ();
120:
121: if (src == null) {
122: throw new IllegalArgumentException(
123: "Must supply non-null SQLJoinOperator instance for src param.");
124: }
125:
126: try {
127: copyFrom(src);
128: } catch (Exception ex) {
129: throw new BaseException(
130: "can not create SQLJoinOperatorImpl "
131: + "using copy constructor", ex);
132: }
133: }
134:
135: /**
136: * @see org.netbeans.modules.sql.framework.model.SQLConnectableObject#addInput
137: */
138: public void addInput(String argName, SQLObject newInput)
139: throws BaseException {
140: if (argName == null || newInput == null) {
141: throw new BaseException("Input arguments not specified");
142: }
143:
144: int newType = newInput.getObjectType();
145: String objType = TagParserUtility.getDisplayStringFor(newType);
146:
147: switch (newType) {
148: case SQLConstants.JOIN_TABLE:
149: if (!(LEFT.equals(argName) || RIGHT.equals(argName))) {
150: throw new BaseException(
151: objType
152: + " is valid only for LEFT and RIGHT input fields.");
153: }
154: break;
155:
156: case SQLConstants.SOURCE_TABLE:
157: case SQLConstants.TARGET_TABLE:
158: if (!(LEFT.equals(argName) || RIGHT.equals(argName))) {
159: throw new BaseException(
160: objType
161: + " is valid only for LEFT and RIGHT input fields.");
162: }
163: break;
164:
165: case SQLConstants.JOIN:
166: if (!(LEFT.equals(argName) || RIGHT.equals(argName))) {
167: throw new BaseException(
168: objType
169: + " is valid only for LEFT and RIGHT input fields.");
170: }
171: // join which is added to this join is no longer a root join
172: // add the this object as parent to newInput join
173: ((SQLJoinOperator) newInput).setRoot(this );
174: break;
175: default:
176: // Redundant, as isInputValid should have caught any
177: // unrecognized types...but left in as a backstop.
178: throw new BaseException("Cannot link " + objType + " '"
179: + newInput.getDisplayName() + "' as input to '"
180: + argName + "' in "
181: + TagParserUtility.getDisplayStringFor(this .type)
182: + " '" + this .getDisplayName() + "'");
183: }
184:
185: SQLInputObject inputObject = (SQLInputObject) this .inputMap
186: .get(argName);
187: if (inputObject != null) {
188: inputObject.setSQLObject(newInput);
189: } else {
190: throw new BaseException("Input with argName '" + argName
191: + "' does not exist.");
192: }
193: }
194:
195: /**
196: * Overrides default implementation.
197: *
198: * @return clone of this instance
199: * @throws CloneNotSupportedException if error occurs during cloning.
200: */
201: public Object clone() throws CloneNotSupportedException {
202: SQLJoinOperatorImpl join = null;
203: try {
204: join = new SQLJoinOperatorImpl(this );
205: } catch (Exception ex) {
206: mLogger.errorNoloc(mLoc.t(
207: "PRSR117: can not create clone of{0}", this
208: .toString()), ex);
209: throw new CloneNotSupportedException(
210: "can not create clone of " + this .toString());
211: }
212:
213: return join;
214: }
215:
216: /**
217: * All SQL objects are cloneable.
218: *
219: * @return clone of this instance
220: * @throws CloneNotSupportedException if error occurs during cloning.
221: */
222: public Object cloneSQLObject() throws CloneNotSupportedException {
223: return this .clone();
224: }
225:
226: /**
227: * Overrides parent implementation to use special rules for determining equality of
228: * two SQLJoinOperators.
229: *
230: * @param o Object to compare against this for equality
231: * @return true if this equals o, false otherwise
232: */
233: public boolean equals(Object o) {
234: if (o == null) {
235: return false;
236: } else if (o == this ) {
237: return true;
238: } else if (!(o instanceof SQLJoinOperator)) {
239: return false;
240: }
241:
242: SQLJoinOperatorImpl target = (SQLJoinOperatorImpl) o;
243:
244: // Must re-implement selected portions of equals(Object) methods in the
245: // AbstractSQLObject hierarchy, rather than calling super.equals(),
246: // as join is a special case.
247: boolean response = (type == target.type);
248: // check for display name
249: response &= (this .getDisplayName() != null) ? this
250: .getDisplayName().equals(target.getDisplayName())
251: : (target.getDisplayName() == null);
252:
253: response &= (attributes != null) ? attributes
254: .equals(target.attributes)
255: : (target.attributes == null);
256:
257: response &= (this .getJoinType() == target.getJoinType());
258: response &= (this .isRoot() == target.isRoot());
259:
260: SQLInputObject leftIn = getInput(LEFT);
261: SQLObject left = (leftIn != null) ? leftIn.getSQLObject()
262: : null;
263: SQLInputObject rightIn = getInput(RIGHT);
264: SQLObject right = (rightIn != null) ? rightIn.getSQLObject()
265: : null;
266:
267: SQLInputObject targetLeftIn = target.getInput(LEFT);
268: SQLObject targetLeft = (targetLeftIn != null) ? targetLeftIn
269: .getSQLObject() : null;
270: SQLInputObject targetRightIn = target.getInput(RIGHT);
271: SQLObject targetRight = (targetRightIn != null) ? targetRightIn
272: .getSQLObject() : null;
273:
274: boolean leftEqualsTargetLeft = (left != null) ? left
275: .equals(targetLeft) : (targetLeft == null);
276: boolean rightEqualsTargetRight = (right != null) ? right
277: .equals(targetRight) : (targetRight == null);
278: boolean leftEqualsTargetRight = (left != null) ? left
279: .equals(targetRight) : (targetRight == null);
280: boolean rightEqualsTargetLeft = (right != null) ? right
281: .equals(targetLeft) : (targetLeft == null);
282:
283: switch (getJoinType()) {
284: case SQLConstants.INNER_JOIN:
285: response &= (leftEqualsTargetLeft && rightEqualsTargetRight)
286: || (rightEqualsTargetLeft && leftEqualsTargetRight);
287: break;
288:
289: default:
290: response &= leftEqualsTargetLeft && rightEqualsTargetRight;
291: break;
292: }
293:
294: response &= (target.getJoinCondition() != null) ? target
295: .getJoinCondition().equals(this .getJoinCondition())
296: : (this .getJoinCondition() == null);
297: return response;
298: }
299:
300: /**
301: * get a list of all tables which are used in this join or any of its input join. This
302: * method recursively goes through LEFT and RIGHT inputs if they are join operator and
303: * finds out all the SourceTables
304: *
305: * @return list of all participating SourceTables for this join
306: */
307: public List getAllSourceTables() {
308: return getAllSourceTables(this );
309: }
310:
311: /**
312: * Gets GUI-related attributes for this instance in the form of a GuiInfo instance.
313: *
314: * @return associated GuiInfo instance
315: * @see GUIInfo
316: */
317: public GUIInfo getGUIInfo() {
318: return guiInfo;
319: }
320:
321: /**
322: * get join condition
323: *
324: * @return join condition
325: */
326: public SQLCondition getJoinCondition() {
327: return jCondition;
328: }
329:
330: /**
331: * get the type for join condition it will be one of following
332: * SYSTEM_DEFINED_CONDITION USER_DEFINED_CONDITION NO_CONDITION
333: *
334: * @return join condition type
335: */
336: public int getJoinConditionType() {
337: Integer jConditionType = (Integer) this
338: .getAttributeObject(ATTR_JOINCONDITION_TYPE);
339: if (jConditionType != null) {
340: return jConditionType.intValue();
341: }
342:
343: return NO_CONDITION;
344: }
345:
346: /**
347: * Get type of join (inner, left outer, right outer, full outer)
348: *
349: * @return type of join.
350: * @see SQLConstants
351: */
352: public int getJoinType() {
353: return ((Integer) getAttributeObject(ATTR_JOINTYPE)).intValue();
354: }
355:
356: public String getJoinTypeString() {
357: String joinType = "";
358: switch (getJoinType()) {
359: case SQLConstants.INNER_JOIN:
360: joinType = "INNER JOIN";
361: break;
362: case SQLConstants.RIGHT_OUTER_JOIN:
363: joinType = "RIGHT OUTER JOIN";
364: break;
365: case SQLConstants.LEFT_OUTER_JOIN:
366: joinType = "LEFT OUTER JOIN";
367: break;
368: case SQLConstants.FULL_OUTER_JOIN:
369: joinType = "FULL OUTER JOIN";
370: }
371: return joinType;
372: }
373:
374: public void setJoinType(String joinType) {
375: if (joinType.equals("INNER JOIN")) {
376: setJoinType(SQLConstants.INNER_JOIN);
377: } else if (joinType.equals("RIGHT OUTER JOIN")) {
378: setJoinType(SQLConstants.RIGHT_OUTER_JOIN);
379: } else if (joinType.equals("LEFT OUTER JOIN")) {
380: setJoinType(SQLConstants.LEFT_OUTER_JOIN);
381: } else if (joinType.equals("FULL OUTER JOIN")) {
382: setJoinType(SQLConstants.FULL_OUTER_JOIN);
383: }
384: }
385:
386: /**
387: * Overrides default implementation to compute hashcode based on values of
388: * non-transient member variables.
389: *
390: * @return hashcode for this instance
391: */
392: public int hashCode() {
393: int hashCode = 0;
394:
395: hashCode = super .hashCode();
396:
397: if (this .getJoinCondition() != null) {
398: hashCode += this .getJoinCondition().hashCode();
399: }
400:
401: hashCode += this .getJoinConditionType();
402:
403: hashCode += ((this .isRoot()) ? 1 : 0);
404: return hashCode;
405: }
406:
407: /**
408: * @see org.netbeans.modules.sql.framework.model.SQLConnectableObject#isInputValid
409: */
410: public int isInputCompatible(String argName, SQLObject input) {
411: return super .isInputCompatible(argName, input);
412: }
413:
414: /**
415: * @see org.netbeans.modules.sql.framework.model.SQLConnectableObject#isInputValid
416: */
417: public boolean isInputValid(String argName, SQLObject input) {
418: if (input == null) {
419: return false;
420: }
421:
422: switch (input.getObjectType()) {
423: case SQLConstants.SOURCE_TABLE:
424: case SQLConstants.TARGET_TABLE:
425: case SQLConstants.JOIN:
426: return (LEFT.equals(argName) || RIGHT.equals(argName));
427:
428: default:
429: return false;
430: }
431: }
432:
433: /**
434: * method isRoot returns true if the root is set.
435: *
436: * @return boolean true if root is set.
437: */
438: public boolean isRoot() {
439: return rootJoin == null ? true : false;
440: }
441:
442: /**
443: * Parses the given Element
444: *
445: * @param xmlElement to be parsed
446: * @exception BaseException while parsing
447: */
448: public void parseXML(Element xmlElement) throws BaseException {
449: super .parseXML(xmlElement);
450:
451: NodeList conditionNodeList = xmlElement
452: .getElementsByTagName(SQLCondition.TAG_CONDITION);
453: if (conditionNodeList != null
454: && conditionNodeList.getLength() != 0) {
455: Element elem = (Element) conditionNodeList.item(0);
456: this .jCondition = SQLModelObjectFactory.getInstance()
457: .createSQLCondition(JOIN_CONDITION);
458: this .jCondition.setParent(this );
459: this .jCondition.parseXML(elem);
460: }
461:
462: NodeList inputNodeList = xmlElement.getChildNodes();
463:
464: if (inputNodeList != null && inputNodeList.getLength() != 0) {
465: for (int i = 0; i < inputNodeList.getLength(); i++) {
466: Node node = inputNodeList.item(i);
467: if (node.getNodeName().equals(SQLObject.TAG_INPUT)) {
468: TagParserUtility
469: .parseInputTag(this , (Element) node);
470: }
471: }
472: }
473:
474: NodeList guiInfoList = xmlElement
475: .getElementsByTagName(GUIInfo.TAG_GUIINFO);
476: if (guiInfoList != null && guiInfoList.getLength() != 0) {
477: Element elem = (Element) guiInfoList.item(0);
478: guiInfo = new GUIInfo(elem);
479: }
480: }
481:
482: /**
483: * @see org.netbeans.modules.sql.framework.model.SQLConnectableObject#removeInputByArgName
484: */
485: public SQLObject removeInputByArgName(String argName,
486: SQLObject sqlObj) throws BaseException {
487: SQLObject retObj = super .removeInputByArgName(argName, sqlObj);
488: if (sqlObj != null
489: && sqlObj.getObjectType() == SQLConstants.JOIN) {
490: // join which is removed again becomes a root join
491: // remove the this object as parent to newInput join
492: ((SQLJoinOperator) sqlObj).setRoot(null);
493: }
494: return retObj;
495: }
496:
497: /**
498: * Second parse, being called, if not found in first pass
499: *
500: * @param element to be parsed
501: * @exception BaseException thrown while secondparsing
502: */
503: public void secondPassParse(Element element) throws BaseException {
504: TagParserUtility.parseInputTag(this , element);
505: }
506:
507: /**
508: * set the join condition
509: *
510: * @param condition join condition
511: */
512: public void setJoinCondition(SQLCondition condition) {
513: this .jCondition = condition;
514: if (this .jCondition != null) {
515: this .jCondition.setDisplayName(JOIN_CONDITION);
516: this .jCondition.setParent(this );
517: }
518: }
519:
520: /**
521: * Sets join condition type to the given value.
522: *
523: * @param jConditionType new value representing join condition type.
524: */
525: public void setJoinConditionType(int jConditionType) {
526: this .setAttribute(ATTR_JOINCONDITION_TYPE, new Integer(
527: jConditionType));
528: }
529:
530: /**
531: * Sets the join type to the given value
532: *
533: * @param newType new join type
534: */
535: public void setJoinType(int newType) {
536: setAttribute(ATTR_JOINTYPE, new Integer(newType));
537: }
538:
539: /**
540: * Sets root join operator to which this join operator is attached.
541: *
542: * @param rJoin root join operator; null if this operator is root
543: */
544: public void setRoot(SQLJoinOperator rJoin) {
545: this .rootJoin = rJoin;
546: }
547:
548: /**
549: * Overrides parent implementation to append GUIInfo information.
550: *
551: * @param prefix String to append to each new line of the XML representation
552: * @return XML representation of this SQLObject instance
553: * @throws BaseException if error occurs during XML creation
554: */
555: public String toXMLString(String prefix) throws BaseException {
556: StringBuilder buffer = new StringBuilder();
557:
558: buffer.append(prefix).append(getHeader());
559: buffer.append(toXMLAttributeTags(prefix));
560:
561: if (this .jCondition != null) {
562: buffer.append(this .jCondition.toXMLString(prefix + "\t"));
563: }
564:
565: buffer.append(TagParserUtility.toXMLInputTag(prefix + "\t",
566: this .inputMap));
567: buffer.append(this .guiInfo.toXMLString(prefix + "\t"));
568: buffer.append(prefix).append(getFooter());
569:
570: return buffer.toString();
571: }
572:
573: public void visit(SQLVisitor visitor) {
574: visitor.visit(this );
575: }
576:
577: private void copyFrom(SQLJoinOperator src) throws BaseException {
578: super .copyFromSource(src);
579: try {
580: // copy gui info
581: GUIInfo gInfo = src.getGUIInfo();
582: this .guiInfo = gInfo != null ? (GUIInfo) gInfo.clone()
583: : null;
584:
585: // copy join type
586: // copy join condition
587: SQLCondition srcCondition = src.getJoinCondition();
588: SQLCondition joinCond = srcCondition != null ? (SQLCondition) srcCondition
589: .cloneSQLObject()
590: : null;
591: // this will set the parent object as this join on condition
592: this .setJoinCondition(joinCond);
593: } catch (Exception ex) {
594: throw new BaseException(
595: "exception occured while cloning SQLJoinOperator",
596: ex);
597: }
598: }
599:
600: private ArrayList getAllSourceTables(SQLJoinOperator join) {
601: ArrayList sTables = new ArrayList();
602:
603: SQLInputObject leftInObj = join.getInput(SQLJoinOperator.LEFT);
604: SQLInputObject rightInObj = join
605: .getInput(SQLJoinOperator.RIGHT);
606:
607: SQLObject leftObj = leftInObj.getSQLObject();
608: SQLObject rightObj = rightInObj.getSQLObject();
609:
610: if (leftObj != null) {
611: if (leftObj.getObjectType() == SQLConstants.JOIN) {
612: sTables
613: .addAll(getAllSourceTables((SQLJoinOperator) leftObj));
614: } else {
615: SQLJoinTable jTable = (SQLJoinTable) leftObj;
616: SourceTable sTable = jTable.getSourceTable();
617: sTables.add(sTable);
618: }
619: }
620:
621: if (rightObj != null) {
622: if (rightObj.getObjectType() == SQLConstants.JOIN) {
623: sTables
624: .addAll(getAllSourceTables((SQLJoinOperator) rightObj));
625: } else {
626: SQLJoinTable jTable = (SQLJoinTable) rightObj;
627: SourceTable sTable = jTable.getSourceTable();
628: sTables.add(sTable);
629: }
630: }
631:
632: return sTables;
633: }
634: }
|