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.Iterator;
045: import java.util.List;
046:
047: import org.w3c.dom.Element;
048: import org.w3c.dom.Node;
049: import org.w3c.dom.NodeList;
050:
051: import org.netbeans.modules.sql.framework.common.utils.TagParserUtility;
052: import org.netbeans.modules.sql.framework.codegen.OperatorInstance;
053: import org.netbeans.modules.sql.framework.codegen.SQLOperatorFactory;
054: import org.netbeans.modules.sql.framework.model.SQLConstants;
055: import org.netbeans.modules.sql.framework.model.SQLGenericOperator;
056: import org.netbeans.modules.sql.framework.model.SQLInputObject;
057: import org.netbeans.modules.sql.framework.model.SQLLiteral;
058: import org.netbeans.modules.sql.framework.model.SQLObject;
059: import org.netbeans.modules.sql.framework.model.SQLOperator;
060: import org.netbeans.modules.sql.framework.model.SQLOperatorArg;
061: import org.netbeans.modules.sql.framework.model.SQLOperatorDefinition;
062: import org.netbeans.modules.sql.framework.ui.graph.IOperatorField;
063: import org.netbeans.modules.sql.framework.ui.graph.IOperatorXmlInfo;
064: import org.netbeans.modules.sql.framework.ui.graph.impl.CustomOperatorNode;
065: import com.sun.sql.framework.exception.BaseException;
066: import com.sun.sql.framework.jdbc.SQLUtils;
067:
068: /**
069: * @author Girish Patil
070: * @version $Revision$
071: */
072:
073: public class SQLCustomOperatorImpl extends SQLGenericOperatorImpl {
074: private static final String OPT_ARGS_TAG = "args";
075: private static final String USER_FUNCTION_ID = "userFx";
076: private static final String PREFIX_ARG = "arg";
077: private static final String NAME_TAG = "name";
078: private static final String JDBC_TYPE_TAG = "jdbc-type";
079: private static final String OUTPUT_TAG = "output";
080:
081: public SQLCustomOperatorImpl() throws BaseException {
082: super ();
083: this .type = SQLConstants.CUSTOM_OPERATOR;
084: this .setDbSpecificOperator(USER_FUNCTION_ID);
085: this .setAttribute(ATTR_CUSTOM_OPERATOR, Boolean.TRUE);
086: }
087:
088: public SQLCustomOperatorImpl(SQLGenericOperator src)
089: throws BaseException {
090: this .type = SQLConstants.CUSTOM_OPERATOR;
091: super .copyFrom(src);
092: if (this .operatorDefinition == null) {
093: this .setDbSpecificOperator(USER_FUNCTION_ID);
094: }
095: }
096:
097: public void setDbSpecificOperator(String operatorName) {
098: this .operatorDefinition = new OperatorInstance(operatorName);
099: }
100:
101: public void setOperatorXmlInfo(IOperatorXmlInfo opInfo)
102: throws BaseException {
103: super .operatorXmlInfo = opInfo;
104: String aType = opInfo.getName();
105: setAttribute(SQLOperator.ATTR_SCRIPTREF, aType);
106: this .hasVariableArgs = false;
107:
108: List outputFields = opInfo.getOutputFields();
109: Iterator iter = outputFields.iterator();
110: if (iter.hasNext()) {
111: IOperatorField field = (IOperatorField) iter.next();
112: String outputType = (String) field
113: .getAttributeValue("retTypeStr");
114: this .operatorDefinition.setOutputJdbcSQLType(outputType);
115: }
116: }
117:
118: public int getOutputJdbcType() {
119: String outputType = null;
120: List outputFields = operatorXmlInfo.getOutputFields();
121: Iterator iter = outputFields.iterator();
122: if (iter.hasNext()) {
123: IOperatorField field = (IOperatorField) iter.next();
124: outputType = (String) field.getAttributeValue("retTypeStr");
125:
126: }
127: return SQLUtils.getStdJdbcType(outputType);
128: }
129:
130: /**
131: * @see org.netbeans.modules.sql.framework.model.SQLConnectableObject#addInput
132: */
133: public void addInput(String argName, SQLObject newInput)
134: throws BaseException {
135: if (argName == null) {
136: throw new BaseException("Input arguments not specified");
137: }
138:
139: if (newInput != null) {
140: int newType = newInput.getObjectType();
141: String objType = TagParserUtility
142: .getDisplayStringFor(newType);
143:
144: if (!isInputValid(argName, newInput)) {
145: throw new BaseException("Cannot link " + objType + " '"
146: + newInput.getDisplayName() + "' as input to '"
147: + argName + "' in "
148: + TagParserUtility.getDisplayStringFor(type)
149: + " '" + this .getDisplayName() + "'");
150: }
151: }
152:
153: SQLInputObject inputObject = (SQLInputObject) this .inputMap
154: .get(argName);
155:
156: if (inputObject != null) {
157: inputObject.setSQLObject(newInput);
158: } else {
159: inputObject = new SQLInputObjectImpl(argName, displayName
160: + "_" + argName, null);
161: inputObject.setSQLObject(newInput);
162: inputMap.put(argName, inputObject);
163: }
164: }
165:
166: public Object clone() throws CloneNotSupportedException {
167: try {
168: SQLCustomOperatorImpl op = new SQLCustomOperatorImpl(this );
169: return op;
170: } catch (BaseException ex) {
171: throw new CloneNotSupportedException(
172: "can not create clone of " + this .getOperatorType());
173: }
174: }
175:
176: public String getCustomOperatorName() {
177: String custOpName = (String) getAttributeObject(ATTR_CUSTOM_OPERATOR_NAME);
178: if (custOpName == null) {
179: custOpName = this .displayName;
180: }
181: return custOpName;
182: }
183:
184: /**
185: * returns the default custom operator type userFx
186: * @return String
187: */
188: public String getOperatorType() {
189: return this .USER_FUNCTION_ID;
190: }
191:
192: /**
193: * Always returns false, as once this object is created, arguments are fixed for this
194: * instance.
195: */
196: public boolean hasVariableArgs() {
197: return false;
198: }
199:
200: public boolean isCustomOperator() {
201: Boolean custOp = (Boolean) getAttributeObject(ATTR_CUSTOM_OPERATOR);
202: if (custOp == null) {
203: custOp = Boolean.FALSE;
204: }
205: return custOp.booleanValue();
206: }
207:
208: /**
209: * @see SQLObject#getObjectType
210: */
211: public int getObjectType() {
212: return type;
213: }
214:
215: //public int isInputCompatible(String argName, SQLObject input) {
216: // return SQLConstants.TYPE_CHECK_COMPATIBLE;
217: //}
218:
219: public SQLObject removeInputByArgName(String argName,
220: SQLObject sqlObj) throws BaseException {
221: Iterator it = inputMap.keySet().iterator();
222: while (it.hasNext()) {
223: String name = (String) it.next();
224: SQLInputObject inputObject = (SQLInputObject) inputMap
225: .get(name);
226: if (inputObject != null) {
227: SQLObject sqlObject = inputObject.getSQLObject();
228: if (sqlObject != null && sqlObject.equals(sqlObj)) {
229: SQLInputObject obj = (SQLInputObject) inputMap
230: .remove(name);
231: if (obj != null) {
232: obj.setSQLObject(null);
233: }
234: return sqlObject;
235: }
236: }
237: }
238: return null;
239: }
240:
241: public void setArgument(String argName, Object val)
242: throws BaseException {
243: if (val instanceof String) {
244: String strVal = (String) val;
245: int argJdbc = this .operatorDefinition
246: .getArgJdbcSQLType(PREFIX_ARG);
247: SQLLiteral literal = new SQLLiteralImpl(strVal, strVal,
248: argJdbc);
249: this .addInput(argName, literal);
250: } else if (val instanceof SQLObject) {
251: this .addInput(argName, (SQLObject) val);
252: } else {
253: throw new BaseException("Can not set argument, object "
254: + val + "is not a valid SQLObject");
255: }
256: }
257:
258: public void setArguments(List opArgs) throws BaseException {
259: this .inputMap.clear();
260: if (operatorDefinition == null) {
261: throw new BaseException("Operator Definition is null.");
262: }
263:
264: if (opArgs != null) {
265: // now add inputs from the argument list
266: Iterator it = opArgs.iterator();
267: /** argument names are always arg1, arg2 etc. **/
268: String argName = "arg";
269: int argIdx = 1;
270: while (it.hasNext()) {
271: SQLObject argValue = (SQLObject) it.next();
272: setArgument(argName + argIdx, argValue);
273: argIdx++;
274: }
275: }
276: }
277:
278: public void setCustomOperator(boolean custOp) {
279: setAttribute(ATTR_CUSTOM_OPERATOR, new Boolean(custOp));
280: }
281:
282: public void setCustomOperatorName(String custOpName) {
283: setAttribute(ATTR_CUSTOM_OPERATOR_NAME, custOpName);
284: setDbSpecificOperator(custOpName);
285: this .setDisplayName(custOpName);
286: }
287:
288: public void initializeInputs(int numOfInputs) throws BaseException {
289: this .inputMap.clear();
290: if (numOfInputs > 0) {
291: for (int i = 1; i <= numOfInputs; i++) {
292: this .addInput(PREFIX_ARG + i, null);
293: }
294: }
295: }
296:
297: /**
298: * Overrides parent implementation to append GUIInfo information.
299: *
300: * @param prefix String to append to each new line of the XML representation
301: * @return XML representation of this SQLObject instance
302: */
303: public String toXMLString(String prefix) {
304: StringBuilder buffer = new StringBuilder(500);
305: if (prefix == null) {
306: prefix = "";
307: }
308:
309: buffer.append(prefix).append(getHeader());
310: buffer.append(toXMLAttributeTags(prefix));
311: this .appendOperatorDefinition(buffer, prefix + '\t');
312: buffer.append(TagParserUtility.toXMLInputTag(prefix + '\t',
313: this .inputMap));
314: buffer.append(this .getGUIInfo().toXMLString(prefix + '\t'));
315: buffer.append(prefix).append(getFooter());
316:
317: return buffer.toString();
318: }
319:
320: public void parseXML(Element xmlElement) throws BaseException {
321: super .parseXML(xmlElement);
322: NodeList nodes = xmlElement.getElementsByTagName(OPT_ARGS_TAG);
323: if (nodes.getLength() > 0) {
324: List inputArgs = new ArrayList();
325: SQLOperatorArg retType = null;
326: NodeList args = nodes.item(0).getChildNodes();
327: for (int i = 0; i < args.getLength(); i++) {
328: Node node = args.item(i);
329: if (node.getNodeType() == Node.ELEMENT_NODE) {
330: Element element = (Element) node;
331: if (PREFIX_ARG.equals(element.getTagName())) {
332: String name = element.getAttribute(NAME_TAG);
333: String jdbcTypeStr = element
334: .getAttribute(JDBC_TYPE_TAG);
335: int jdbcType = SQLUtils
336: .getStdJdbcType(jdbcTypeStr);
337: SQLOperatorArg optArg = new SQLOperatorArg(
338: name, jdbcType);
339: inputArgs.add(optArg);
340: } else if (OUTPUT_TAG.equals(element.getTagName())) {
341: String jdbcTypeStr = element
342: .getAttribute(JDBC_TYPE_TAG);
343: int jdbcType = SQLUtils
344: .getStdJdbcType(jdbcTypeStr);
345: retType = new SQLOperatorArg("return", jdbcType);
346: }
347: }
348: }
349:
350: CustomOperatorNode customOptNode = new CustomOperatorNode(
351: this .getOperatorXmlInfo(), inputArgs, retType);
352: this .setOperatorXmlInfo(customOptNode);
353: this .getOperatorDefinition().setArgList(inputArgs);
354: }
355: }
356:
357: private void appendOperatorDefinition(StringBuilder buffer,
358: String prefix) {
359: SQLOperatorDefinition optDef = this .getOperatorDefinition();
360: Iterator it = optDef.getArgList().iterator();
361: //Write out input arguments
362: buffer.append(prefix).append('<').append(OPT_ARGS_TAG).append(
363: '>').append('\n');
364: while (it.hasNext()) {
365: SQLOperatorArg optArg = (SQLOperatorArg) it.next();
366: buffer.append(prefix + '\t').append('<').append(PREFIX_ARG);
367: buffer.append(' ').append(NAME_TAG).append("=\"").append(
368: optArg.getArgName()).append('"');
369: buffer
370: .append(' ')
371: .append(JDBC_TYPE_TAG)
372: .append("=\"")
373: .append(
374: SQLUtils
375: .getStdSqlType(optArg.getJdbcType()))
376: .append('"');
377: buffer.append("/>\n");
378: }
379: //Write out return type
380: int jdbcType = this .getOutputJdbcType();
381: buffer.append(prefix + '\t').append('<').append(OUTPUT_TAG);
382: buffer.append(' ').append(JDBC_TYPE_TAG).append("=\"").append(
383: SQLUtils.getStdSqlType(jdbcType)).append('"');
384: buffer.append("/>\n");
385: buffer.append(prefix).append("</").append(OPT_ARGS_TAG).append(
386: '>').append('\n');
387: }
388:
389: /**
390: * Determines if input referenced by the given argument name can received a link from
391: * the given SQLObject without breaking type casting rules.
392: *
393: * @param argName name of the operator input to which the source operator is being
394: * connected.
395: * @param input SQLObject to which input argument is being connected.
396: * @return true if 'argName' can be connected to input, false otherwise
397: */
398: public int isCastable(String argName, SQLObject input) {
399: int srcType = SQLConstants.JDBCSQL_TYPE_UNDEFINED;
400:
401: int destType = operatorDefinition.getArgJdbcSQLType(argName);
402:
403: // XXX Refactor to completely reflect SQL syntax rules for sql operators.
404: switch (input.getObjectType()) {
405: case SQLConstants.GENERIC_OPERATOR:
406: case SQLConstants.CUSTOM_OPERATOR:
407: case SQLConstants.CAST_OPERATOR:
408: case SQLConstants.DATE_DIFF_OPERATOR:
409: case SQLConstants.DATE_ADD_OPERATOR:
410: case SQLConstants.SOURCE_COLUMN:
411: case SQLConstants.VISIBLE_LITERAL:
412: case SQLConstants.COLUMN_REF:
413: case SQLConstants.CASE:
414: srcType = input.getJdbcType();
415: break;
416:
417: case SQLConstants.LITERAL:
418: return SQLConstants.TYPE_CHECK_SAME;
419: default:
420: srcType = SQLConstants.JDBCSQL_TYPE_UNDEFINED;
421: }
422:
423: return SQLOperatorFactory.getDefault().getCastingRuleFor(
424: srcType, destType);
425: }
426: }
|