001: /*
002: * hgcommons 7
003: * Hammurapi Group Common Library
004: * Copyright (C) 2003 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz/hammurapi-biz/ef/xmenu/hammurapi-group/products/products/hgcommons/index.html
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.sql.columns;
024:
025: import java.io.Serializable;
026: import java.sql.PreparedStatement;
027: import java.sql.ResultSet;
028: import java.sql.ResultSetMetaData;
029: import java.sql.SQLException;
030: import java.util.HashMap;
031: import java.util.Map;
032:
033: import javax.xml.transform.TransformerException;
034:
035: import org.apache.xpath.XPathAPI;
036: import org.w3c.dom.Element;
037: import org.w3c.dom.Node;
038:
039: import biz.hammurapi.config.ConfigurationException;
040: import biz.hammurapi.config.Context;
041: import biz.hammurapi.config.ContextConfigurable;
042: import biz.hammurapi.config.DomConfigFactory;
043: import biz.hammurapi.config.DomConfigurable;
044: import biz.hammurapi.sql.SQLExceptionEx;
045: import biz.hammurapi.util.Attributable;
046: import biz.hammurapi.xml.dom.AbstractDomObject;
047: import biz.hammurapi.xml.dom.CompositeDomSerializer;
048:
049: /**
050: * Base class for different column types
051: * @author Pavel Vlasov
052: * @version $Revision: 1.14 $
053: */
054: public abstract class Column implements Cloneable, ContextConfigurable,
055: DomConfigurable, Attributable, Serializable {
056:
057: private ColumnChangeListener listener;
058: private String name;
059: private boolean isModified;
060: private boolean isPrimaryKey;
061: private String label;
062:
063: public abstract void setOriginal();
064:
065: /**
066: * Clears modified flag.
067: */
068: public void clearModified() {
069: isModified = false;
070: }
071:
072: /**
073: * Sets field value to default and clears modified flag.
074: *
075: */
076: public abstract void clear();
077:
078: /**
079: * @param listener The listener to set.
080: */
081: public void setListener(ColumnChangeListener listener) {
082: this .listener = listener;
083: }
084:
085: /**
086: * @return Returns the listener.
087: */
088: public ColumnChangeListener getListener() {
089: return listener;
090: }
091:
092: protected void onChange() {
093: isModified = true;
094: if (listener != null) {
095: listener.onChange(this );
096: }
097: }
098:
099: /**
100: * @param name Column name
101: * @param isPrimaryKey 'true' if column is part of primary key
102: */
103: public Column(String name, boolean isPrimaryKey) {
104: super ();
105: this .name = name;
106: this .isPrimaryKey = isPrimaryKey;
107: }
108:
109: /**
110: * @return Column name if value was modified, null otherwise.
111: */
112: public String listName() {
113: return isModified ? name : null;
114: }
115:
116: /**
117: * Parameterizes prepared statement.
118: * @param ps
119: * @param idx Parameter index
120: * @param force If true column is treated a modified
121: * @return idx+1 if column was modified and prepared statement was parameterized, idx otherwise.
122: */
123: public int parameterize(PreparedStatement ps, int idx, boolean force)
124: throws SQLException {
125: if (isModified || force) {
126: try {
127: parameterizeInternal(ps, idx);
128: } catch (SQLException e) {
129: throw new SQLExceptionEx("Could not parameterize "
130: + getName() + ": " + e, e);
131: }
132: return idx + 1;
133: }
134:
135: return idx;
136: }
137:
138: /**
139: * Implement this method in subclasses.
140: */
141: protected abstract void parameterizeInternal(PreparedStatement ps,
142: int idx) throws SQLException;
143:
144: /**
145: * Implement this method in subclasses.
146: */
147: public abstract void parameterizeOriginal(PreparedStatement ps,
148: int idx) throws SQLException;
149:
150: /**
151: * @return Returns the isPrimaryKey.
152: */
153: public boolean isPrimaryKey() {
154: return isPrimaryKey;
155: }
156:
157: /**
158: * @return name
159: */
160: public String getName() {
161: return name;
162: }
163:
164: /**
165: * Returns column value as object
166: * @param originalValue if true this method returns original value instead of current value.
167: * @return
168: */
169: public abstract Object getObjectValue(boolean originalValue);
170:
171: /**
172: * @param owner
173: */
174: public void toDom(Element owner, String nodeName,
175: boolean originalValue) {
176: // String nodeName = domName==null ? name : domName;
177: if (nodeName == null) {
178: nodeName = getName();
179: }
180:
181: Object objectValue = getObjectValue(false);
182:
183: if (nodeName.length() == 0) {
184: // Zero mapping
185: return;
186: }
187:
188: if (nodeName.startsWith("@")) {
189: // Attribute mapping
190: if (objectValue != null) {
191: owner.setAttribute(nodeName.substring(1), objectValue
192: .toString());
193: }
194: } else if (nodeName.equals(".")) {
195: // Text mapping
196: if (objectValue != null) {
197: owner.appendChild(owner.getOwnerDocument()
198: .createTextNode(objectValue.toString()));
199: }
200: } else if (nodeName.startsWith("!")) {
201: // Simple mapping
202: if (objectValue != null) {
203: Element el = owner.getOwnerDocument().createElement(
204: nodeName.substring(1));
205: owner.appendChild(el);
206: el.appendChild(owner.getOwnerDocument().createTextNode(
207: objectValue.toString()));
208: }
209: } else {
210: // full mapping
211: Element el = owner.getOwnerDocument().createElement(
212: nodeName);
213: owner.appendChild(el);
214:
215: CompositeDomSerializer cds = CompositeDomSerializer
216: .getThreadInstance();
217:
218: if (objectValue == null) {
219: el.setAttribute("is-null", "yes");
220: } else {
221: cds.toDomSerializable(objectValue).toDom(el);
222: }
223:
224: if (isPrimaryKey) {
225: el.setAttribute("primary-key", "yes");
226: }
227: if (isModified) {
228: el.setAttribute("modified", "yes");
229: }
230:
231: if (label != null) {
232: el.setAttribute("label", label);
233: }
234:
235: el.setAttribute("column-type", getType());
236:
237: el.setAttribute("align", getAlignment());
238:
239: if (!attributes.isEmpty()) {
240: cds.toDomSerializable(attributes).toDom(
241: AbstractDomObject.addElement(el,
242: "column-attributes"));
243: }
244: }
245: }
246:
247: protected abstract String getType();
248:
249: /**
250: * @return
251: */
252: protected String getAlignment() {
253: return "left";
254: }
255:
256: /**
257: * @param typeName
258: * @return Column type for Java type
259: */
260: public static Class columnType(String typeName) {
261: if ("boolean".equals(typeName)) {
262: return BooleanColumn.class;
263: } else if ("byte".equals(typeName)) {
264: return ByteColumn.class;
265: } else if ("char".equals(typeName)) {
266: return CharColumn.class;
267: } else if ("double".equals(typeName)) {
268: return DoubleColumn.class;
269: } else if ("float".equals(typeName)) {
270: return FloatColumn.class;
271: } else if ("int".equals(typeName)) {
272: return IntColumn.class;
273: } else if ("long".equals(typeName)) {
274: return LongColumn.class;
275: } else if ("short".equals(typeName)) {
276: return ShortColumn.class;
277: } else {
278: return ObjectColumn.class;
279: }
280: }
281:
282: /**
283: * @return Returns the isModified.
284: */
285: public boolean isModified() {
286: return isModified;
287: }
288:
289: protected boolean force;
290:
291: // private String domName;
292:
293: /**
294: * @param force
295: */
296: public void setForce(boolean force) {
297: this .force = force;
298: }
299:
300: /**
301: * @param rs
302: * @param name
303: * @return True if result set has a column with specified name.
304: * @throws SQLException
305: */
306: public static boolean hasColumn(ResultSet rs, String name)
307: throws SQLException {
308: ResultSetMetaData metaData = rs.getMetaData();
309: for (int i = 1, c = metaData.getColumnCount(); i <= c; i++) {
310: if (name.equals(metaData.getColumnName(i))) {
311: return true;
312: }
313: }
314: return false;
315: }
316:
317: /**
318: * Loads value from XML Element
319: * @param textValue Text value
320: */
321: public abstract void load(String textValue);
322:
323: public Object clone() throws CloneNotSupportedException {
324: return super .clone();
325: }
326:
327: /**
328: * @return display label for column
329: */
330: public String getLabel() {
331: return label;
332: }
333:
334: /**
335: * Sets display label for column
336: * @param label
337: */
338: public void setLabel(String label) {
339: this .label = label;
340: }
341:
342: // /**
343: // * @param domName
344: // */
345: // public void setDomName(String domName) {
346: // this.domName=domName;
347: // }
348: //
349: // public String getDomName() {
350: // return domName;
351: // }
352:
353: /**
354: * Copies values from source column
355: * @param source
356: */
357: public abstract void set(Column source);
358:
359: private Map attributes = new HashMap();
360:
361: public void setAttribute(Object key, Object value) {
362: attributes.put(key, value);
363: }
364:
365: public Object getAttribute(Object key) {
366: return attributes.get(key);
367: }
368:
369: public Object removeAttribute(Object key) {
370: return attributes.remove(key);
371: }
372:
373: public void configure(Node configNode, Context context)
374: throws ConfigurationException {
375: if (!((Element) configNode).getAttribute("is-null").equals(
376: "yes")) {
377: load(AbstractDomObject.getElementText(configNode));
378: }
379:
380: DomConfigFactory dcf = new DomConfigFactory();
381: attributes.clear();
382: try {
383: Node attributesNode = XPathAPI.selectSingleNode(configNode,
384: "column-attributes");
385: if (attributesNode != null) {
386: attributes.putAll((Map) dcf.create(attributesNode));
387: }
388: } catch (TransformerException e) {
389: throw new ConfigurationException(
390: "Cannot load column attributes: " + e, e);
391: }
392:
393: }
394: }
|