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.mashup.db.common;
042:
043: import java.lang.reflect.Constructor;
044: import java.util.HashMap;
045: import java.util.Iterator;
046: import java.util.Map;
047: import net.java.hulp.i18n.Logger;
048: import com.sun.sql.framework.utils.StringUtil;
049: import org.netbeans.modules.etl.logger.Localizer;
050: import org.netbeans.modules.etl.logger.LogUtil;
051:
052: /**
053: * Simple class to bind property key, type, current value, required flag and (optional)
054: * default value information for a key-value property.
055: *
056: * @author Jonathan Giron
057: * @version $Revision$
058: */
059: public class Property implements Cloneable, Comparable {
060:
061: /* Log4J category string */
062: private static final String LOG_CATEGORY = Property.class.getName();
063:
064: /* Key name */
065: private String name;
066:
067: /* Property type */
068: private Class type;
069:
070: /* Flag indicating whether a value is required */
071: private boolean required;
072:
073: /* Refrsh column definition if this this property has been changed */
074: private boolean refresh;
075:
076: /* (Optional) default value */
077: private Object defaultValue;
078:
079: /* Current property value */
080: private Object value;
081: private static transient final Logger mLogger = LogUtil
082: .getLogger(Property.class.getName());
083: private static transient final Localizer mLoc = Localizer.get();
084:
085: /** Creates a new default instance of Property */
086: public Property() {
087: required = true;
088: defaultValue = null;
089: refresh = true;
090: }
091:
092: /**
093: * Creates a new instance of Property with the given key name, type, and required
094: * value flag.
095: *
096: * @param myKey key name
097: * @param myType value type
098: * @param isRequired true if required, false otherwise
099: */
100: public Property(String myKey, Class myType, boolean isRequired) {
101: this ();
102: name = myKey;
103: type = myType;
104: required = isRequired;
105: }
106:
107: /**
108: * Creates a new instance of Property with the given key name, type, default value,
109: * and required value flag.
110: *
111: * @param myKey key name
112: * @param myType value type
113: * @param myDefault default value
114: * @param isRefreshRequired true if required, false otherwise
115: */
116: public Property(String myKey, Class myType, String myDefault,
117: boolean isRefreshRequired) {
118: this (myKey, myType, true);
119: setDefault(myDefault);
120: setRefresh(isRefreshRequired);
121: }
122:
123: public Property(String myKey, Class myType, String myDefault) {
124: this (myKey, myType, true);
125: setDefault(myDefault);
126: }
127:
128: /**
129: * Creates a Map of pure key-value mappings from the given Map of keys to Property
130: * instances. All other object classes are ignored.
131: *
132: * @param properties Map of Property instances to be transformed
133: * @return new Map of simple key-value mappings, based on contents of properties
134: */
135: public static Map createKeyValueMapFrom(Map properties) {
136: Map newMap = new HashMap();
137:
138: Iterator propIter = properties.entrySet().iterator();
139: while (propIter.hasNext()) {
140: Map.Entry entry = (Map.Entry) propIter.next();
141: Object obj = entry.getValue();
142:
143: if (obj instanceof Property) {
144: Property prop = (Property) obj;
145: if (prop.getValue() instanceof String) {
146: newMap.put(prop.getName(), StringUtil
147: .escapeControlChars(prop.getValue()
148: .toString()));
149: } else {
150: newMap.put(prop.getName(), prop.getValue());
151: }
152: }
153: }
154:
155: return newMap;
156: }
157:
158: /**
159: * Gets property type
160: *
161: * @return type
162: */
163: public Class getType() {
164: return this .type;
165: }
166:
167: /**
168: * Sets Java type of this property's value object.
169: *
170: * @param newType new type (e.g., Foo.class.getName() for instance of Foo)
171: */
172: public void setType(String newType) {
173: try {
174: type = Class.forName(newType);
175: } catch (ClassNotFoundException ex) {
176: ex.printStackTrace();
177: type = String.class;
178: }
179:
180: }
181:
182: /**
183: * Gets name of this property.
184: *
185: * @return name of this property
186: */
187: public String getName() {
188: return name;
189: }
190:
191: /**
192: * Sets name of this property.
193: *
194: * @param newName new name of this property
195: */
196: public void setName(String newName) {
197: name = newName;
198: }
199:
200: public void setRefresh(boolean isRefreshRequired) {
201: refresh = isRefreshRequired;
202: }
203:
204: public boolean isRefreshRequired() {
205: return refresh;
206: }
207:
208: /**
209: * Gets current value of property
210: *
211: * @return current value
212: */
213: public Object getValue() {
214: return (value != null) ? value : defaultValue;
215: }
216:
217: /**
218: * Sets current value of property to the given object. If newValue is null and this is
219: * a required property, an IllegalArgumentException is thrown unless a default value
220: * is defined. If newValue cannot be converted to this Property's given type, a
221: * ClassCastException is thrown.
222: *
223: * @param newValue new value of property.
224: */
225: public void setValue(Object newValue) {
226: if (newValue == null) {
227: if ((defaultValue == null) && required) {
228: throw new IllegalArgumentException(
229: "Must supply non-null value for required property.");
230: }
231: newValue = null;
232: return;
233: }
234:
235: Class newValClass = newValue.getClass();
236: if (type == newValClass) {
237: if (type == String.class) {
238: value = StringUtil.unescapeControlChars(newValue
239: .toString());
240: } else {
241: value = newValue;
242: }
243: } else {
244: try {
245: String unescaped = StringUtil
246: .unescapeControlChars(newValue.toString());
247: Constructor constr = type
248: .getConstructor(new Class[] { String.class });
249: value = constr.newInstance(new Object[] { unescaped });
250: } catch (Exception ex) {
251: throw new ClassCastException("Value of type "
252: + newValClass.getName()
253: + " cannot be assigned to this property.");
254: }
255: }
256: }
257:
258: /**
259: * Gets default value, if any, for this property.
260: *
261: * @return default value, or null if none is set.
262: */
263: public Object getDefault() {
264: return defaultValue;
265: }
266:
267: /**
268: * Sets default value, if any, for this property.
269: *
270: * @param def default value, or null for no default
271: */
272: public void setDefault(String def) {
273: String cooked = StringUtil.unescapeControlChars(def);
274:
275: try {
276: Constructor constr = type
277: .getConstructor(new Class[] { String.class });
278: this .defaultValue = constr
279: .newInstance(new Object[] { cooked });
280: } catch (Exception ex) {
281: mLogger
282: .errorNoloc(
283: mLoc
284: .t(
285: "PRSR052: Could not construct default value of type {0}with parameter = {1}",
286: type, def), ex);
287: defaultValue = null;
288: }
289: }
290:
291: /**
292: * Indicates whether this property requires a non-null, valid value.
293: *
294: * @return true if valid value is required, false otherwise
295: */
296: public boolean isRequired() {
297: return required;
298: }
299:
300: /**
301: * Sets whether this property requires a non-null, valid value.
302: *
303: * @param required true if valid value is required, false otherwise
304: */
305: public void setRequired(String required) {
306: this .required = Boolean.valueOf(required).booleanValue();
307: }
308:
309: /**
310: * Indicates whether the current value of this property is valid, based on its type.
311: *
312: * @return true if valid, false otherwise.
313: */
314: public boolean isValid() {
315: return isValidValue(getValue());
316: }
317:
318: /**
319: * Indicates whether the given value would be a valid value for this property.
320: *
321: * @param aValue value to test
322: * @return true if valid, false otherwise.
323: */
324: public boolean isValidValue(Object aValue) {
325: if (aValue == null) {
326: return !required;
327: }
328:
329: return (type == aValue.getClass());
330: }
331:
332: /**
333: * Puts key-value information to the given Map, provided that the value is valid.
334: *
335: * @param aMap Map to receive this instance's key-value information
336: * @return true if key-value information is valid and put succeeded, false otherwise.
337: */
338: public boolean putValueTo(Map aMap) {
339: if (isValid()) {
340: aMap.put(name, getValue());
341: return true;
342: }
343:
344: return false;
345: }
346:
347: /**
348: * Gets key-value information from the given Map, provided that the value in the map
349: * would be a valid value.
350: *
351: * @param aMap Map from which to get this instance's key-value information
352: * @return true if key-value information is valid and get succeeded, false otherwise.
353: */
354: public boolean getValueFrom(Map aMap) {
355: Object o = aMap.get(name);
356: Object newValue = null;
357:
358: if (o instanceof Property) {
359: newValue = ((Property) o).getValue();
360: } else {
361: newValue = o;
362: }
363:
364: if (isValidValue(newValue)) {
365: setValue(newValue);
366: return true;
367: }
368:
369: return false;
370: }
371:
372: /**
373: * Overrides default implementation to return value, if any, of this property.
374: *
375: * @return value of property, or null if no value currently exists
376: */
377: public String toString() {
378: return (getValue() != null) ? getValue().toString() : null;
379: }
380:
381: /**
382: * Gets key-value pair in canonical Java properties format, e.g., "key=value".
383: *
384: * @return key-value pair in canonical Java properties format, or empty string if
385: * current value is invalid
386: */
387: public String getKeyValuePair() {
388: String myValue = (getValue() != null) ? StringUtil
389: .escapeControlChars(getValue().toString()) : "";
390:
391: return (isValid()) ? (name + "='"
392: + StringUtil.replaceInString(myValue, "'", "\'") + "'")
393: : "";
394: }
395:
396: /**
397: * Overrides default implementation to allow cloning of this instance.
398: *
399: * @return clone of this instance
400: */
401: public Object clone() {
402: try {
403: return (Property) super .clone();
404: } catch (CloneNotSupportedException e) {
405: throw new InternalError(e.toString());
406: }
407: }
408:
409: /**
410: * Overrides default implementation.
411: *
412: * @param o Object to compare for equality against this instance.
413: * @return true if o is equivalent to this, false otherwise
414: */
415: public boolean equals(Object o) {
416: if (o == this ) {
417: return true;
418: } else if (null == o) {
419: return false;
420: }
421:
422: boolean response = false;
423: if (o instanceof Property) {
424: response = true;
425: Property src = (Property) o;
426:
427: response &= (name != null) ? name.equals(src.name)
428: : (src.name == null);
429:
430: // NOTE: don't include defaultValue or required in comparison
431: // as they are used in determining response for getValue()
432: Object myValue = getValue();
433: Object srcValue = src.getValue();
434: response &= (myValue != null) ? myValue.equals(srcValue)
435: : (srcValue == null);
436:
437: response &= (type == src.type);
438: }
439:
440: return response;
441: }
442:
443: /**
444: * Overrides default implementation to compute its value based on member variables.
445: *
446: * @return computed hash code
447: */
448: public int hashCode() {
449: int hashCode = 0;
450:
451: hashCode += (name != null) ? name.hashCode() : 0;
452: hashCode += type.hashCode();
453: hashCode += (getValue() != null) ? getValue().hashCode() : 0;
454:
455: // NOTE: don't include defaultValue or required in computation
456: // as they are used in determining response for getValue()
457: return hashCode;
458: }
459:
460: /**
461: * Compares this object with the specified object for order. Returns a negative
462: * integer, zero, or a positive integer as this object is less than, equal to, or
463: * greater than the specified object.
464: * <p>
465: * Note: this class has a natural ordering that is inconsistent with equals.
466: *
467: * @param o the Object to be compared.
468: * @return a negative integer, zero, or a positive integer as this object is less
469: * than, equal to, or greater than the specified object.
470: * @throws ClassCastException if the specified object's type prevents it from being
471: * compared to this Object.
472: */
473: public int compareTo(Object o) {
474: if (o == this ) {
475: return 0;
476: } else if (o == null) {
477: return -1;
478: }
479:
480: Property p = (Property) o;
481: return name.compareTo(p.getName());
482: }
483: }
|