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.codegen;
042:
043: import java.io.Serializable;
044: import java.util.Collection;
045: import java.util.HashMap;
046: import java.util.Iterator;
047: import java.util.List;
048: import java.util.Map;
049: import org.netbeans.modules.sql.framework.common.jdbc.SQLUtils;
050: import org.netbeans.modules.sql.framework.common.utils.XmlUtil;
051: import org.netbeans.modules.sql.framework.model.SQLConstants;
052: import org.netbeans.modules.sql.framework.model.SQLOperatorDefinition;
053: import org.w3c.dom.Element;
054: import org.w3c.dom.NodeList;
055: import com.sun.sql.framework.exception.BaseException;
056: import net.java.hulp.i18n.Logger;
057: import com.sun.sql.framework.utils.StringUtil;
058: import org.netbeans.modules.etl.logger.Localizer;
059: import org.netbeans.modules.etl.logger.LogUtil;
060:
061: /**
062: * Factory for creating instances of SQLOperatorDefinition.
063: *
064: * @author Ahimanikya Satapathy
065: * @version $Revision$
066: */
067: public class SQLOperatorFactory implements Serializable {
068:
069: private static final String LOG_CATEGORY = SQLOperatorFactory.class
070: .getName();
071: private static SQLOperatorFactory instance;
072: /* Reference to default operatordef info file. */
073: private static final String BASE_OPERATOR_DEFINITION_FILE = "org/netbeans/modules/sql/framework/codegen/base/config/operator-script.xml";
074: /* Child node gag name for operators list */
075: private static final String TAG_OPERATOR_LIST = "operators";
076: /* Constant for operator defn metadata tag. */
077: private static final String TAG_OPERATOR = "operator";
078: /* Constant for name attribute. */
079: private static final String ATTR_NAME = "name";
080: /* Child node tag name for casting rules */
081: private static final String TAG_CASTING_RULES = "casting-rules";
082: /* Maps canonical operator names to SQLOperatorDefinition implementations */
083: private Map<String, OperatorInstance> operatorMap = null;
084: /* Maps canonical operator names to applicable casting rules */
085: private Map<String, CastingRule> castingRules = null;
086: private SQLOperatorFactory parent;
087: private static transient final Logger mLogger = LogUtil
088: .getLogger(SQLOperatorFactory.class.getName());
089: private static transient final Localizer mLoc = Localizer.get();
090:
091: /**
092: * Gets default SQLOperatorFactory.
093: *
094: * @return SQLOperatorFactory
095: */
096: public static SQLOperatorFactory getDefault() {
097: if (instance == null) {
098: instance = new SQLOperatorFactory(
099: BASE_OPERATOR_DEFINITION_FILE, null);
100: }
101:
102: return instance;
103: }
104:
105: /**
106: * Constructor.
107: *
108: * @param defFile String definition file name
109: * @param parent SQLOperatorFactory
110: */
111: public SQLOperatorFactory(String defFile, SQLOperatorFactory parent) {
112: operatorMap = new HashMap<String, OperatorInstance>();
113: castingRules = new HashMap<String, CastingRule>();
114:
115: this .parent = parent;
116:
117: try {
118: parseXML(defFile);
119: } catch (BaseException e) {
120: mLogger
121: .errorNoloc(
122: mLoc
123: .t("PRSR088: getSQLOperatorFactory ERROR:,Failed to read the Operator Template File:"),
124: e);
125:
126: }
127: }
128:
129: /**
130: * Gets applicable casting rule for the given source and target JDBC types.
131: *
132: * @param sourceType source JDBC typecode
133: * @param targetType target JDBCD typecode
134: * @return applicable rule constant, one of TYPE_CHECK_SAME, TYPE_CHECK_COMPATIBLE,
135: * TYPE_CHECK_DOWNCAST_WARNING, or TYPE_CHECK_INCOMPATIBLE.
136: * @see SQLConstants
137: */
138: public int getCastingRuleFor(int sourceType, int targetType) {
139: String srcTypeStr = SQLUtils.getStdSqlType(sourceType);
140: String trgTypeStr = SQLUtils.getStdSqlType(targetType);
141:
142: if (StringUtil.isNullString(srcTypeStr)
143: || StringUtil.isNullString(trgTypeStr)) {
144: return SQLConstants.TYPE_CHECK_INCOMPATIBLE;
145: }
146: return getCastingRuleFor(srcTypeStr, trgTypeStr);
147: }
148:
149: /**
150: * Gets applicable casting rule for the given source and target SQL types.
151: *
152: * @param sourceType source SQL type
153: * @param targetType target SQL type
154: * @return applicable rule constant, one of TYPE_CHECK_SAME, TYPE_CHECK_COMPATIBLE,
155: * TYPE_CHECK_DOWNCAST_WARNING, or TYPE_CHECK_INCOMPATIBLE.
156: * @see SQLConstants
157: */
158: public int getCastingRuleFor(String sourceType, String targetType) {
159: if (StringUtil.isNullString(sourceType)) {
160: throw new IllegalArgumentException(
161: "Must supply non-empty String value for sourceType");
162: } else if (StringUtil.isNullString(targetType)) {
163: throw new IllegalArgumentException(
164: "Must supply non-empty String value for targetType");
165: }
166:
167: if (sourceType.trim().equalsIgnoreCase(targetType.trim())) {
168: return SQLConstants.TYPE_CHECK_SAME;
169: }
170:
171: CastingRule ruleSet = castingRules.get(sourceType.trim()
172: .toLowerCase());
173:
174: if (ruleSet == null && parent != null) {
175: return parent.getCastingRuleFor(sourceType, targetType);
176: }
177:
178: return (ruleSet != null) ? ruleSet.getRule(targetType.trim()
179: .toLowerCase()) : SQLConstants.TYPE_CHECK_INCOMPATIBLE;
180: }
181:
182: /**
183: * Gets Operator definition for a given operator by name.
184: *
185: * @param operatorName String
186: * @return SQLOperatorDefinition for <code>operatorName</code>, or null if none
187: * exists.
188: */
189: public SQLOperatorDefinition getSQLOperatorDefinition(
190: String operatorName) {
191: if (StringUtil.isNullString(operatorName)) {
192: throw new IllegalArgumentException(
193: "Must supply non-empty String value for operatorType");
194: }
195:
196: SQLOperatorDefinition def = (SQLOperatorDefinition) operatorMap
197: .get(operatorName);
198: if (def == null && parent != null) {
199: def = parent.getSQLOperatorDefinition(operatorName);
200: }
201:
202: return def;
203: }
204:
205: /**
206: * Gets a DB-specific SQLOperatorDefinition, if any, for a given operator by name.
207: *
208: * @param dbOpName String
209: * @return SQLOperatorDefinition for <code>dbOpName</code>, or null if none exists.
210: */
211: public SQLOperatorDefinition getDbSpecficOperatorDefinition(
212: String dbOpName) {
213: Iterator it = operatorMap.values().iterator();
214: SQLOperatorDefinition opDef = null;
215:
216: while (it.hasNext()) {
217: opDef = (SQLOperatorDefinition) it.next();
218: if (opDef.getDbSpecficName().equals(dbOpName)) {
219: return opDef;
220: }
221: opDef = null;
222: }
223:
224: if (opDef == null && parent != null) {
225: opDef = parent.getDbSpecficOperatorDefinition(dbOpName);
226: }
227:
228: return opDef;
229: }
230:
231: /**
232: * Parses the XML for OperatorDefinition.
233: *
234: * @param defFile String Definition filename
235: * @exception BaseException thrown while loading OperatorDefinitions
236: */
237: protected void parseXML(String defFile) throws BaseException {
238: try {
239: Element opListElement = XmlUtil.loadXMLFile(defFile);
240: NodeList opDefsList = opListElement
241: .getElementsByTagName(TAG_OPERATOR_LIST);
242: if (opDefsList.getLength() != 0) {
243: Element listElement = (Element) opDefsList.item(0);
244: NodeList defList = listElement
245: .getElementsByTagName(TAG_OPERATOR);
246: for (int i = 0; i < defList.getLength(); i++) {
247: OperatorInstance defn = new OperatorInstance();
248: defn.parseXML((Element) defList.item(i));
249: operatorMap.put(defn.getOperatorName(), defn);
250: }
251: }
252:
253: NodeList castRuleNode = opListElement
254: .getElementsByTagName(TAG_CASTING_RULES);
255: if (castRuleNode.getLength() != 0) {
256: Element castRuleElement = (Element) castRuleNode
257: .item(0);
258: NodeList ruleList = castRuleElement
259: .getElementsByTagName(CastingRule.TAG_RULE_MAP);
260:
261: for (int i = 0; i < ruleList.getLength(); i++) {
262: Element entryElement = (Element) ruleList.item(i);
263: try {
264: CastingRule rule = new CastingRule();
265: rule.parseXML(entryElement);
266: castingRules.put(rule.getSourceType(), rule);
267: } catch (BaseException e) {
268: continue;
269: }
270: }
271: }
272: } catch (Exception e) {
273: mLogger
274: .errorNoloc(
275: mLoc
276: .t(
277: "PRSR089: Failed to read the Operator TemplateFile:{0}",
278: LOG_CATEGORY), e);
279:
280: }
281: }
282:
283: /**
284: * Holds a casting rule map between a given source type and various possibilities for
285: * target types.
286: *
287: * @author Jonathan Giron
288: */
289: protected static class CastingRule {
290:
291: /** Child node tag name for casting rule definition */
292: public static final String TAG_RULE_MAP = "rule-map"; //NOI18N
293: /* Attribute name for source-type string */
294: private static final String ATTR_SOURCE_TYPE = "source-type"; //NOI18N
295: /* Child node tag name for rule tag */
296: private static final String TAG_RULE = "rule"; //NOI18N
297: /* Attribute name for target types string list */
298: private static final String ATTR_TARGET_TYPES = "target-types"; //NOI18N
299: private static final String VALUE_EQUIVALENT = "equivalent"; //NOI18N
300: private static final String VALUE_UPCAST = "upcast"; //NOI18N
301: private static final String VALUE_DOWNCAST = "downcast"; //NOI18N
302: private String sourceTypeName;
303: private Map<String, String> typeToRuleMap;
304:
305: /**
306: * Default Constructor
307: */
308: public CastingRule() {
309: typeToRuleMap = new HashMap<String, String>();
310: }
311:
312: /**
313: * Constructor
314: *
315: * @param typeName for a given type create Rule
316: */
317: public CastingRule(String typeName) {
318: this ();
319: if (StringUtil.isNullString(typeName)) {
320: throw new IllegalArgumentException(
321: "Must supply non-empty String value for typeName.");
322: }
323:
324: sourceTypeName = typeName;
325: }
326:
327: /**
328: * Creates new instance of CastingRule using given typeName, rule value, and
329: * target types.
330: *
331: * @param typeName String representing type name
332: * @param ruleVal int representing rule
333: * @param targetTypes Collection of target types
334: */
335: public CastingRule(String typeName, int ruleVal,
336: Collection targetTypes) {
337: this (typeName);
338:
339: Iterator iter = targetTypes.iterator();
340: while (iter.hasNext()) {
341: String type = (String) iter.next();
342: setRule(type, ruleVal);
343: }
344: }
345:
346: /**
347: * Gets source type for this rule
348: *
349: * @return source type
350: */
351: public String getSourceType() {
352: return sourceTypeName;
353: }
354:
355: /**
356: * Sets rule value to be associated with the given type.
357: *
358: * @param newType name of new type add to this rule.
359: * @param ruleVal rule value to apply for <code>newType</code>
360: */
361: public void setRule(String newType, int ruleVal) {
362: if (StringUtil.isNullString(newType)) {
363: throw new IllegalArgumentException(
364: "Must supply non-empty String value for newType.");
365: }
366:
367: switch (ruleVal) {
368: case SQLConstants.TYPE_CHECK_SAME:
369: case SQLConstants.TYPE_CHECK_COMPATIBLE:
370: case SQLConstants.TYPE_CHECK_INCOMPATIBLE:
371: case SQLConstants.TYPE_CHECK_DOWNCAST_WARNING:
372: typeToRuleMap.put(newType.trim().toLowerCase(), String
373: .valueOf(ruleVal));
374: break;
375: default:
376: throw new IllegalArgumentException(
377: "Unrecognized casting rule value: " + ruleVal);
378: }
379: }
380:
381: /**
382: * Gets rule value associated with the given type.
383: *
384: * @param typeName name of type to be casted
385: * @return applicable rule value for the given incoming type.
386: */
387: public int getRule(String typeName) {
388: Object o = typeToRuleMap.get(typeName);
389: if (o instanceof String) {
390: try {
391: return Integer.parseInt((String) o);
392: } catch (NumberFormatException e) {
393: return SQLConstants.TYPE_CHECK_INCOMPATIBLE;
394: }
395: }
396: return SQLConstants.TYPE_CHECK_INCOMPATIBLE;
397: }
398:
399: /**
400: * Parses XML representation of a CastingRule as represented by the given DOM
401: * element.
402: *
403: * @param element DOM element representing a CastingRule definition
404: * @throws BaseException if error occurs during parsing.
405: */
406: public void parseXML(Element element) throws BaseException {
407: if (element == null
408: || !TAG_RULE_MAP.equals(element.getNodeName())) {
409: throw new BaseException("Null or invalid element.");
410: }
411:
412: sourceTypeName = element.getAttribute(ATTR_SOURCE_TYPE);
413: if (StringUtil.isNullString(sourceTypeName)) {
414: throw new BaseException(
415: "CastingRule element must contain source name.");
416: }
417:
418: NodeList outcomeList = element
419: .getElementsByTagName(TAG_RULE);
420: for (int j = 0; j < outcomeList.getLength(); j++) {
421: Element outcomeElement = (Element) outcomeList.item(j);
422: String ruleName = outcomeElement
423: .getAttribute(ATTR_NAME);
424: if (StringUtil.isNullString(ruleName)) {
425: continue;
426: }
427:
428: int ruleVal = SQLConstants.TYPE_CHECK_UNKNOWN;
429: if (VALUE_EQUIVALENT.equals(ruleName)) {
430: ruleVal = SQLConstants.TYPE_CHECK_SAME;
431: } else if (VALUE_UPCAST.equals(ruleName)) {
432: ruleVal = SQLConstants.TYPE_CHECK_COMPATIBLE;
433: } else if (VALUE_DOWNCAST.equals(ruleName)) {
434: ruleVal = SQLConstants.TYPE_CHECK_DOWNCAST_WARNING;
435: }
436:
437: List typesList = StringUtil
438: .createStringListFrom(outcomeElement
439: .getAttribute(ATTR_TARGET_TYPES));
440: Iterator typeIter = typesList.iterator();
441: while (typeIter.hasNext()) {
442: String newType = (String) typeIter.next();
443: setRule(newType, ruleVal);
444: }
445: }
446: }
447: }
448: }
|