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:
042: package com.sun.rave.faces.data;
043:
044: import com.sun.rave.faces.util.ComponentBundle;
045: import java.beans.Beans;
046: import java.sql.ResultSet;
047: import java.sql.ResultSetMetaData;
048: import java.sql.SQLException;
049: import java.sql.Types;
050: import java.util.ArrayList;
051: import java.util.List;
052: import java.util.Map;
053: import javax.faces.FacesException;
054: import javax.faces.el.EvaluationException;
055: import javax.faces.el.PropertyNotFoundException;
056: import javax.faces.el.PropertyResolver;
057: import javax.faces.model.SelectItem;
058:
059: /**
060: * This custom Property Resolver handles the special case of resolving ResultSet columns into editable
061: * data values. This also enables binding to a SelectItem array from a ResultSet for filling lists
062: * and dropdowns.
063: *
064: * These expressions are supported:
065: *
066: * #{...myResultSet.currentRow['COLUMN_NAME']}
067: * --> binds to the 'COLUMN_NAME' column of the current row of the ResultSet
068: *
069: * #(...myResultSet.selectItems['COLUMN_NAME'])
070: * #(...myResultSet.selectItems['VALUE_COLUMN_NAME,LABEL_COLUMN_NAME'])
071: * #(...myResultSet.selectItems['VALUE_COLUMN_NAME,LABEL_COLUMN_NAME,DESC_COLUMN_NAME'])
072: * --> binds to an array of SelectItem generated by iterating through the ResultSet
073: */
074: public class ResultSetPropertyResolver extends PropertyResolver {
075:
076: private static final ComponentBundle bundle = ComponentBundle
077: .getBundle(ResultSetPropertyResolver.class);
078:
079: public static final String CURRENT_ROW_KEY = "currentRow"; // NOI18N
080: public static final String SELECT_ITEMS_KEY = "selectItems"; // NOI18N
081:
082: protected PropertyResolver nested;
083:
084: public ResultSetPropertyResolver(PropertyResolver nested) {
085: this .nested = nested;
086: }
087:
088: public Object getValue(Object base, Object property)
089: throws EvaluationException, PropertyNotFoundException {
090: if (base instanceof ResultSet) {
091: if (CURRENT_ROW_KEY.equals(property)) {
092: return new RowData((ResultSet) base);
093: }
094: if (SELECT_ITEMS_KEY.equals(property)) {
095: return new ColumnData((ResultSet) base);
096: }
097: } else if (base instanceof RowData) {
098: return ((RowData) base).getData(property.toString());
099: } else if (base instanceof ColumnData) {
100: return ((ColumnData) base).getSelectItems(property
101: .toString());
102: }
103: Object o = nested.getValue(base, property);
104: if (o instanceof ResultSet) {
105: initResultSet((ResultSet) o);
106: }
107: return o;
108: }
109:
110: public Object getValue(Object base, int index)
111: throws EvaluationException, PropertyNotFoundException {
112: Object o = nested.getValue(base, index);
113: if (o instanceof ResultSet) {
114: initResultSet((ResultSet) o);
115: }
116: return o;
117: }
118:
119: public void setValue(Object base, Object property, Object value)
120: throws EvaluationException, PropertyNotFoundException {
121: if (base instanceof RowData) {
122: ((RowData) base).setData(property.toString(), value);
123: return;
124: }
125: nested.setValue(base, property, value);
126: }
127:
128: public void setValue(Object base, int index, Object value)
129: throws EvaluationException, PropertyNotFoundException {
130: nested.setValue(base, index, value);
131: }
132:
133: public boolean isReadOnly(Object base, Object property)
134: throws EvaluationException, PropertyNotFoundException {
135: if (base instanceof ResultSet
136: && (CURRENT_ROW_KEY.equals(property) || SELECT_ITEMS_KEY
137: .equals(property))) {
138: return true;
139: } else if (base instanceof RowData) {
140: return false;
141: } else if (base instanceof ColumnData) {
142: return true;
143: }
144: return nested.isReadOnly(base, property);
145: }
146:
147: public boolean isReadOnly(Object base, int index)
148: throws EvaluationException, PropertyNotFoundException {
149: return nested.isReadOnly(base, index);
150: }
151:
152: public Class getType(Object base, Object property)
153: throws EvaluationException, PropertyNotFoundException {
154: if (base instanceof ResultSet) {
155: if (CURRENT_ROW_KEY.equals(property)) {
156: return RowData.class;
157: }
158: if (SELECT_ITEMS_KEY.equals(property)) {
159: return ColumnData.class;
160: }
161: } else if (base instanceof RowData) {
162: return ((RowData) base).getDataType(property.toString());
163: } else if (base instanceof ColumnData) {
164: return ArrayList.class;
165: } else if (base instanceof DataCache.Row
166: && property instanceof String) { //fix 6222537
167: Class typeAccordingToNested = nested
168: .getType(base, property);
169: if (typeAccordingToNested != null) {
170: return typeAccordingToNested;
171: }
172: DataCache.Row row = (DataCache.Row) base;
173: DataCache.Column[] columns = row.getColumns();
174: //find the column, and return its java type
175: for (int i = 0; columns != null && i < columns.length; i++) {
176: if (((String) property).equalsIgnoreCase(columns[i]
177: .getColumnName())) {
178: return columns[i].getJavaType();
179: }
180: }
181: //we know nested.getType(base, property) will return null,
182: //and that's what we would call next, so just return it here
183: return null;
184: }
185: return nested.getType(base, property);
186: }
187:
188: public Class getType(Object base, int index)
189: throws EvaluationException, PropertyNotFoundException {
190: return nested.getType(base, index);
191: }
192:
193: ////////////////////////////////////////////////////////////////////////////////////////////////
194: ////////////////////////////////////////////////////////////////////////////////////////////////
195:
196: public class RowData {
197:
198: protected ResultSet resultSet;
199: protected ResultSetMetaData metadata;
200: protected ArrayList columnNameList;
201:
202: public RowData(ResultSet resultSet) {
203: this .resultSet = resultSet;
204: try {
205: metadata = resultSet.getMetaData();
206: columnNameList = new ArrayList();
207: int cols = metadata.getColumnCount();
208: for (int i = 1; i <= cols; i++) {
209: columnNameList.add(metadata.getColumnName(i));
210: }
211: } catch (SQLException x) {
212: throw new FacesException(x);
213: }
214: }
215:
216: public Class getDataType(String column)
217: throws PropertyNotFoundException {
218: if (!columnNameList.contains(column)) {
219: throw new PropertyNotFoundException(
220: "Invalid column name: " + column); //NOI18N
221: }
222: try {
223: return Class.forName(metadata
224: .getColumnClassName(columnNameList
225: .indexOf(column) + 1));
226: } catch (Exception x) {
227: return null;
228: }
229: }
230:
231: public Object getData(String column)
232: throws PropertyNotFoundException {
233: if (!columnNameList.contains(column)) {
234: throw new PropertyNotFoundException(
235: "Invalid column name: " + column);
236: }
237: try {
238: if (Beans.isDesignTime()) {
239: return getFakeData(metadata, column);
240: } else {
241: initResultSet(resultSet);
242: return resultSet.getObject(column);
243: }
244: } catch (SQLException e) {
245: throw new FacesException(e);
246: }
247: }
248:
249: public Object setData(String column, Object value)
250: throws PropertyNotFoundException {
251: if (!columnNameList.contains(column)) {
252: throw new PropertyNotFoundException(
253: "Invalid column name: " + column);
254: }
255: try {
256: Object previous;
257: if (!Beans.isDesignTime()) {
258: initResultSet(resultSet);
259: previous = resultSet.getObject(column);
260: if ((previous == null) && (value == null)) {
261: return previous;
262: } else if ((previous != null) && (value != null)
263: && previous.equals(value)) {
264: return previous;
265: }
266: resultSet.updateObject(column, value);
267: return previous;
268: } else {
269: previous = getFakeData(metadata, column);
270: return previous;
271: }
272: } catch (SQLException e) {
273: throw new FacesException(e);
274: }
275: }
276: }
277:
278: ////////////////////////////////////////////////////////////////////////////////////////////////
279: ////////////////////////////////////////////////////////////////////////////////////////////////
280:
281: public class ColumnData {
282: protected ResultSet resultSet;
283: protected ResultSetMetaData metadata;
284:
285: public ColumnData(ResultSet resultSet) {
286: this .resultSet = resultSet;
287: try {
288: this .metadata = resultSet.getMetaData();
289: } catch (SQLException x) {
290: throw new FacesException(x);
291: }
292: }
293:
294: public Object getSelectItems(String columns) {
295: /*
296: * returns a List of Objects or SelectItems
297: *
298: * (examples based on PERSON database table):
299: *
300: * "NAME" -->
301: * returns a List filled with SelectItem objects,
302: * with the 'itemValue' set to NAME's values
303: *
304: * "PERSONID,NAME" -->
305: * returns a List filled with SelectItem objects,
306: * with the 'itemValue' set to PERSONID's values,
307: * and the 'itemLabel' set to NAME's values
308: *
309: * "PERSONID,NAME,JOBTITLE" -->
310: * returns a List filled with SelectItem objects,
311: * with the 'itemValue' set to PERSONID's values,
312: * the 'itemLabel' set to NAME's values,
313: * and the 'itemDescription' set to JOBTITLE's values
314: *
315: * Any cases that are out-of-scope throw IllegalArgumentException
316: */
317: String itemValueName = null;
318: String itemLabelName = null;
319: String itemDescriptionName = null;
320:
321: //MBOHM fix 5086833
322: //could have internal commas, in say, selectItems['employee.employeeid, employee.firstname || \' , \' || employee.lastname']
323: List cols = new ArrayList();
324: String col;
325: boolean quoteOpen = false;
326: int currStart = 0;
327: for (int i = 0; i < columns.length(); i++) {
328: char c = columns.charAt(i);
329: if (c == '\'') {
330: quoteOpen = !quoteOpen;
331: } else if (c == ',' && !quoteOpen) {
332: col = columns.substring(currStart, i);
333: if (col.length() > 0) {
334: cols.add(col);
335: }
336: currStart = i + 1;
337: }
338: }
339: //get the remaining stuff after the last period
340: if (currStart < columns.length()) {
341: col = columns.substring(currStart);
342: cols.add(col);
343: }
344:
345: //String[] args = columns.split(","); //NOI18N
346: String[] args = (String[]) cols.toArray(new String[cols
347: .size()]);
348: if (args.length < 1) {
349: throw new IllegalArgumentException();
350: }
351: itemValueName = args[0];
352: if (args.length > 1) {
353: itemLabelName = args[1];
354: }
355: if (args.length > 2) {
356: itemDescriptionName = args[2];
357: }
358:
359: ArrayList list = new ArrayList();
360: if (!Beans.isDesignTime()) {
361: try {
362: initResultSet(resultSet);
363: int resultSetIndexSave = resultSet.getRow();
364: resultSet.first();
365: while (!resultSet.isAfterLast()) {
366: if (itemLabelName == null) {
367: list.add(new SelectItem(resultSet
368: .getObject(itemValueName)));
369: } else if (itemDescriptionName == null) {
370: list.add(new SelectItem(resultSet
371: .getObject(itemValueName),
372: resultSet.getObject(itemLabelName)
373: .toString()));
374: } else {
375: list.add(new SelectItem(resultSet
376: .getObject(itemValueName),
377: resultSet.getObject(itemLabelName)
378: .toString(),
379: resultSet.getObject(
380: itemDescriptionName)
381: .toString()));
382: }
383: resultSet.next();
384: }
385: if (resultSetIndexSave > 0) {
386: resultSet.absolute(resultSetIndexSave);
387: } else {
388: resultSet.first();
389: }
390: } catch (SQLException x) {
391: x.printStackTrace();
392: }
393: } else {
394: for (int i = 0; i < 3; i++) {
395: try {
396: if (itemLabelName == null) {
397: list.add(new SelectItem(getFakeData(
398: metadata, itemValueName)));
399: } else if (itemDescriptionName == null) {
400: list
401: .add(new SelectItem(getFakeData(
402: metadata, itemValueName),
403: getFakeData(metadata,
404: itemLabelName)
405: .toString()));
406: } else {
407: list
408: .add(new SelectItem(
409: getFakeData(metadata,
410: itemValueName),
411: getFakeData(metadata,
412: itemLabelName)
413: .toString(),
414: getFakeData(metadata,
415: itemDescriptionName)
416: .toString()));
417: }
418: } catch (SQLException x) {
419: }
420: }
421: }
422:
423: return list;
424: }
425:
426: }
427:
428: ////////////////////////////////////////////////////////////////////////////////////////////////
429: ////////////////////////////////////////////////////////////////////////////////////////////////
430:
431: public static void initResultSet(ResultSet resultSet) {
432:
433: }
434:
435: ////////////////////////////////////////////////////////////////////////////////////////////////
436: ////////////////////////////////////////////////////////////////////////////////////////////////
437:
438: private static Object getFakeData(ResultSetMetaData rsmd,
439: String colName) throws SQLException {
440:
441: int colIndex = -1;
442: for (int i = 1; i <= rsmd.getColumnCount(); i++) {
443: if (rsmd.getColumnName(i).equals(colName)) {
444: colIndex = i;
445: break;
446: }
447: }
448: switch (rsmd.getColumnType(colIndex)) {
449: case Types.ARRAY:
450: return new java.sql.Array() {
451: public Object getArray() {
452: return null;
453: }
454:
455: public Object getArray(long index, int count) {
456: return null;
457: }
458:
459: public Object getArray(long index, int count, Map map) {
460: return null;
461: }
462:
463: public Object getArray(Map map) {
464: return null;
465: }
466:
467: public int getBaseType() {
468: return Types.CHAR;
469: }
470:
471: public String getBaseTypeName() {
472: return "CHAR"; //NOI18N
473: }
474:
475: public ResultSet getResultSet() {
476: return null;
477: }
478:
479: public ResultSet getResultSet(long index, int count) {
480: return null;
481: }
482:
483: public ResultSet getResultSet(long index, int count,
484: Map map) {
485: return null;
486: }
487:
488: public ResultSet getResultSet(Map map) {
489: return null;
490: }
491:
492: public void free() {
493: }
494: };
495: case Types.BIGINT:
496:
497: //return new Long(rowIndex);
498: return new Long(123);
499: case Types.BINARY:
500: return new byte[] { 1, 2, 3, 4, 5 };
501: case Types.BIT:
502: return new Boolean(true);
503: case Types.BLOB:
504: return new javax.sql.rowset.serial.SerialBlob(new byte[] {
505: 1, 2, 3, 4, 5 });
506: case Types.BOOLEAN:
507: return new Boolean(true);
508: case Types.CHAR:
509:
510: //return new String(colName + rowIndex);
511: return new String(bundle.getMessage("arbitraryCharData")); //NOI18N
512: case Types.CLOB:
513: return new javax.sql.rowset.serial.SerialClob(bundle
514: .getMessage("arbitraryClobData").toCharArray());
515: case Types.DATALINK:
516: try {
517: return new java.net.URL("http://www.sun.com"); //NOI18N
518: } catch (java.net.MalformedURLException e) {
519: return null;
520: }
521: case Types.DATE:
522: return new java.sql.Date(new java.util.Date().getTime());
523: case Types.DECIMAL:
524: return new java.math.BigDecimal(java.math.BigInteger.ONE);
525: case Types.DISTINCT:
526: return null;
527: case Types.DOUBLE:
528:
529: //return new Double(rowIndex);
530: return new Double(123);
531: case Types.FLOAT:
532:
533: //return new Double(rowIndex);
534: return new Double(123);
535: case Types.INTEGER:
536:
537: //return new Integer(rowIndex);
538: return new Integer(123);
539: case Types.JAVA_OBJECT:
540:
541: //return new String(colName + "_" + rowIndex); //NOI18N
542: return new String(bundle.getMessage("arbitraryCharData")); //NOI18N
543: case Types.LONGVARBINARY:
544: return new byte[] { 1, 2, 3, 4, 5 };
545: case Types.LONGVARCHAR:
546:
547: //return new String(colName + "_" + rowIndex); //NOI18N
548: return new String(bundle.getMessage("arbitraryCharData")); //NOI18N
549: case Types.NULL:
550: return null;
551: case Types.NUMERIC:
552: return new java.math.BigDecimal(java.math.BigInteger.ONE);
553: case Types.OTHER:
554: return null;
555: case Types.REAL:
556:
557: //return new Float(rowIndex);
558: return new Float(123);
559: case Types.REF:
560: return new java.sql.Ref() {
561: private Object data = new String(bundle
562: .getMessage("arbitraryCharData")); //NOI18N
563:
564: public String getBaseTypeName() {
565: return "CHAR"; //NOI18N
566: }
567:
568: public Object getObject() {
569: return data;
570: }
571:
572: public Object getObject(Map map) {
573: return data;
574: }
575:
576: public void setObject(Object value) {
577: data = value;
578: }
579: };
580: case Types.SMALLINT:
581:
582: //return new Short((short)rowIndex);
583: return new Short((short) 123);
584: case Types.STRUCT:
585: return new java.sql.Struct() {
586: private String[] data = {
587: bundle.getMessage("arbitraryCharData"),
588: bundle.getMessage("arbitraryCharData2"),
589: bundle.getMessage("arbitraryCharData3") }; //NOI18N
590:
591: public Object[] getAttributes() {
592: return data;
593: }
594:
595: public Object[] getAttributes(Map map) {
596: return data;
597: }
598:
599: public String getSQLTypeName() {
600: return "CHAR"; //NOI18N
601: }
602: };
603: case Types.TIME:
604: return new java.sql.Time(new java.util.Date().getTime());
605: case Types.TIMESTAMP:
606: return new java.sql.Timestamp(new java.util.Date()
607: .getTime());
608: case Types.TINYINT:
609:
610: //return new Byte((byte)rowIndex);
611: return new Byte((byte) 123);
612: case Types.VARBINARY:
613: return new byte[] { 1, 2, 3, 4, 5 };
614: case Types.VARCHAR:
615:
616: //return new String(colName + "_" + rowIndex); //NOI18N
617: return new String(bundle.getMessage("arbitraryCharData")); //NOI18N
618: }
619: return null;
620: }
621: }
|