0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package com.sun.data.provider.impl;
0043:
0044: import java.beans.Beans;
0045: import java.beans.PropertyChangeListener;
0046: import java.beans.PropertyChangeEvent;
0047: import java.io.Serializable;
0048: import java.lang.reflect.Method;
0049: import java.math.BigDecimal;
0050: import java.math.BigInteger;
0051: import java.sql.Date;
0052: import java.sql.ResultSet;
0053: import java.sql.ResultSetMetaData;
0054: import java.sql.SQLException;
0055: import java.sql.Time;
0056: import java.sql.Timestamp;
0057: import java.sql.Types;
0058: import java.text.MessageFormat;
0059: import java.util.ArrayList;
0060: import java.util.Iterator;
0061: import java.util.List;
0062: import java.util.Locale;
0063: import java.util.Map;
0064: import java.util.ResourceBundle;
0065: import java.util.TreeMap;
0066: import java.net.MalformedURLException;
0067: import java.net.URL;
0068: import java.net.URLClassLoader;
0069: import java.io.File;
0070: import java.io.FileFilter;
0071: import javax.sql.RowSet;
0072: import javax.sql.RowSetEvent;
0073: import javax.sql.RowSetListener;
0074: import javax.sql.rowset.CachedRowSet;
0075: import javax.sql.rowset.spi.SyncResolver;
0076: import javax.sql.rowset.spi.SyncProviderException;
0077: import com.sun.data.provider.DataProviderException;
0078: import com.sun.data.provider.FieldKey;
0079: import com.sun.data.provider.RowKey;
0080: import com.sun.data.provider.RefreshableDataProvider;
0081: import com.sun.data.provider.RefreshableDataListener;
0082: import com.sun.data.provider.TableCursorVetoException;
0083: import com.sun.data.provider.TableDataProvider;
0084: import com.sun.data.provider.TransactionalDataListener;
0085: import com.sun.data.provider.TransactionalDataProvider;
0086: import com.sun.sql.rowset.CachedRowSetX;
0087: import com.sun.sql.rowset.SyncResolverX;
0088: import java.util.logging.Level;
0089: import java.util.logging.Logger;
0090:
0091: /**
0092: * <p>{@link TableDataProvider} implementation that wraps a <code>CachedRowSet</code>.
0093: * </p>
0094: *
0095: * <p>Note: valueChanged events will only fire for column changes made via the
0096: * CachedRowSetDataProvider</p>
0097: *
0098: * <p>Note: The lifetime of the <code>RowKey</code>s handed out
0099: * <code>CachedRowSetDataProvider</code> is until the underlying <code>CachedRowSet</code>
0100: * is closed or reexecuted.</p>
0101: */
0102: public class CachedRowSetDataProvider extends AbstractTableDataProvider
0103: implements TableDataProvider, TransactionalDataProvider,
0104: RefreshableDataProvider {
0105:
0106: private static Logger LOGGER = Logger
0107: .getLogger(CachedRowSetDataProvider.class.getName());
0108:
0109: // ----------------------------------------------------------- Constructors
0110:
0111: /**
0112: * <p>Construct an unconfigured {@link CachedRowSetDataProvider}.</p>
0113: */
0114: public CachedRowSetDataProvider() {
0115: }
0116:
0117: /**
0118: * <p>Construct a {@link CachedRowSetDataProvider} that wraps the
0119: * specified <code>CachedRowSet</code>.</p>
0120: *
0121: * @param cachedRowSet The <code>CachedRowSet</code> to be wrapped
0122: */
0123: public CachedRowSetDataProvider(CachedRowSet cachedRowSet) {
0124: setCachedRowSet(cachedRowSet);
0125: }
0126:
0127: // ------------------------------------------------------- Static Variables
0128:
0129: /**
0130: * <p>Localized resources for this implementation.</p>
0131: */
0132: private static ResourceBundle bundle = ResourceBundle.getBundle(
0133: "com.sun.data.provider.impl.Bundle", //NOI18N
0134: Locale.getDefault(), CachedRowSetDataProvider.class
0135: .getClassLoader());
0136:
0137: // ----------------------------------------------------- Instance Variables
0138:
0139: /**
0140: * <p>The array of {@link FieldKey}s that correspond to the columns
0141: * represented in the <code>ResultSetMetaData</code> for this
0142: * request.</p>
0143: */
0144: private FieldKey fieldKeys[] = null;
0145:
0146: /**
0147: * <p>Map of the {@link FieldKey}s that correspond to the columns
0148: * represented in the <code>ResultSetMetaData</code> for this
0149: * request, keyed (case-insensitive) by column name.</p>
0150: */
0151: private Map fieldKeysMap = null;
0152:
0153: /**
0154: * <p>The <code>ResultSetMetaData</code> associated with the
0155: * <code>CachedRowSet</code> that we are wrapping.</p>
0156: */
0157: private transient ResultSetMetaData metaData = null;
0158:
0159: /**
0160: * <p>The <code>CachedRowSet</code> that we are wrapping.</p>
0161: */
0162: private CachedRowSet cachedRowSet = null;
0163:
0164: /**
0165: * <p>indicator if we are on the inserted row.
0166: * Note: in the semantics of dataprovider, this
0167: * would be called onAppendRow.
0168: * FIXME: It seems like there should be a CachedRowSet
0169: * method to detect this, but I don't see it.</p>
0170: */
0171: private boolean onInsertRow = false;
0172:
0173: /**
0174: * Set to true if the rowset has been executed
0175: */
0176: private boolean executed = false;
0177:
0178: /**
0179: * Set to true when rowset command had changed
0180: */
0181: private boolean refreshMetaDataFile = false;
0182:
0183: /**
0184: * Store filename for deletion when rowset command changes
0185: */
0186: private String metaDataFilename;
0187:
0188: /**
0189: * <p>{@link PropertyChangeListener} registered with the {@link CachedRowSetX}.</p>
0190: */
0191: private RowSetPropertyChangeListener propertyChangeListener = null;
0192:
0193: /**
0194: * <p>{@link PropertyChangeListener} registered with the {@link CachedRowSetX}.</p>
0195: */
0196: private RowSetListener rowSetListener = null;
0197:
0198: // ------------------------------------------------------------ Properties
0199:
0200: /**
0201: * <p>Return the <code>CachedRowSet</code> that we are wrapping.</p>
0202: */
0203: public CachedRowSet getCachedRowSet() {
0204: return cachedRowSet;
0205: }
0206:
0207: /**
0208: * <p>Set the <code>CachedRowSet</code> that we are wrapping. In addition,
0209: * ensure that the <code>CachedRowSet</code> has been executed so that
0210: * subseuqent calls accessing it will work.</p>
0211: *
0212: * @param cachedRowSet The new <code>CachedRowSet</code>
0213: */
0214: public void setCachedRowSet(CachedRowSet cachedRowSet) {
0215:
0216: // Initialize our internal state information
0217: if (this .cachedRowSet != null
0218: && this .cachedRowSet instanceof CachedRowSetX
0219: && this .propertyChangeListener != null) {
0220: ((CachedRowSetX) this .cachedRowSet)
0221: .removePropertyChangeListener(propertyChangeListener);
0222: propertyChangeListener = null;
0223: }
0224: if (this .cachedRowSet != null && this .rowSetListener != null) {
0225: this .cachedRowSet.removeRowSetListener(rowSetListener);
0226: rowSetListener = null;
0227: }
0228: try {
0229: cursorFirst();
0230: } catch (Exception e) {
0231: }
0232: this .cachedRowSet = cachedRowSet;
0233: metaData = null;
0234: fieldKeys = null;
0235: fieldKeysMap = null;
0236: if (cachedRowSet != null
0237: && cachedRowSet instanceof CachedRowSetX) {
0238: propertyChangeListener = new RowSetPropertyChangeListener();
0239: ((CachedRowSetX) cachedRowSet)
0240: .addPropertyChangeListener(propertyChangeListener);
0241:
0242: if (!Beans.isDesignTime()) {
0243: try {
0244: executed = ((CachedRowSetX) cachedRowSet)
0245: .isExecuted();
0246: } catch (SQLException e) {
0247: LOGGER.log(Level.WARNING, null, e);
0248: executed = false;
0249: }
0250: } else {
0251: // Assume the row set has not yet been executed yet. The worse
0252: // that could happen is the row set was executed before it was
0253: // passed in to the data provider, and we end up executing it
0254: // one more time. Note that this particular branch of logic
0255: // is unlikely because almost always the rowset is a CachedRowSetX
0256: executed = false;
0257: }
0258: }
0259: if (cachedRowSet != null) {
0260: rowSetListener = new CachedRowSetListener();
0261: cachedRowSet.addRowSetListener(rowSetListener);
0262: }
0263: fireProviderChanged();
0264: }
0265:
0266: // --------------------------------------- CachedRowSetDataProvider Methods
0267:
0268: /**
0269: * free resources used by this instance
0270: *
0271: * Close is guaranteed not to throw an exception.
0272: */
0273: public void close() {
0274: if (!Beans.isDesignTime()) {
0275: try {
0276: setCachedRowSet(null);
0277: } catch (Exception e) {
0278: // attempted to cleanup, contract is close() will silently fail
0279: }
0280: }
0281: }
0282:
0283: // ---------------------------------------------------------- RowKey Methods
0284:
0285: /** {@inheritDoc} */
0286: public RowKey[] getRowKeys(int count, RowKey afterRow)
0287: throws DataProviderException {
0288:
0289: /*
0290: * Only hand out RowKeys for rows that have not been deleted
0291: */
0292: if (getCachedRowSet() == null) {
0293: return new CachedRowSetRowKey[0];
0294: }
0295: int cursorIndexSave = getCursorIndex();
0296: List keys = new ArrayList();
0297: try {
0298: int startIndex = 0;
0299: if (afterRow instanceof CachedRowSetRowKey) {
0300: startIndex = ((CachedRowSetRowKey) afterRow).getIndex() + 1;
0301: }
0302: while (absolute(startIndex + 1) && keys.size() < count) {
0303: try {
0304: if (!(isUpdatable() && getCachedRowSet()
0305: .rowDeleted())) {
0306: keys.add(new CachedRowSetRowKey(startIndex));
0307: }
0308: } catch (SQLException e) {
0309: // if sqlexception, we can't hand out a rowkey for this row
0310: }
0311: startIndex++;
0312: }
0313: return (RowKey[]) keys.toArray(new CachedRowSetRowKey[0]);
0314: } finally {
0315: try {
0316: setCursorIndex(cursorIndexSave);
0317: } catch (IllegalArgumentException e) {
0318: // This can happen if the row at the cursorIndex was deleted
0319: }
0320: }
0321: }
0322:
0323: /** {@inheritDoc} */
0324: public RowKey[] getAllRows() throws DataProviderException {
0325: return getRowKeys(Integer.MAX_VALUE, null);
0326: }
0327:
0328: public RowKey getRowKey(String rowId) throws DataProviderException {
0329: return CachedRowSetRowKey.create(rowId);
0330: }
0331:
0332: // ---------------------------------------------------------- Cursor Methods
0333:
0334: /**
0335: * storage for the current cursor row
0336: */
0337: protected RowKey cursorRow = new CachedRowSetRowKey(0);
0338:
0339: protected int getCursorIndex() {
0340: if (cursorRow instanceof CachedRowSetRowKey) {
0341: return ((CachedRowSetRowKey) cursorRow).getIndex();
0342: }
0343: return -1;
0344: }
0345:
0346: private boolean absolute(int index) {
0347: if (getCachedRowSet() == null) {
0348: return false;
0349: }
0350: if (Beans.isDesignTime()) {
0351: return index >= 1 && index <= 3;
0352: }
0353: try {
0354: checkExecute();
0355: boolean saveShowDeleted = getCachedRowSet()
0356: .getShowDeleted();
0357: try {
0358: getCachedRowSet().setShowDeleted(true);
0359: return getCachedRowSet().absolute(index);
0360: } catch (SQLException e) {
0361: return false;
0362: } finally {
0363: getCachedRowSet().setShowDeleted(saveShowDeleted);
0364: }
0365: } catch (SQLException e) {
0366: return false;
0367: }
0368: }
0369:
0370: protected boolean setCursorIndex(int index) {
0371: try {
0372: setCursorRow(new CachedRowSetRowKey(index));
0373: return true;
0374: } catch (TableCursorVetoException tcvx) {
0375: return false;
0376: }
0377: }
0378:
0379: /** {@inheritDoc} */
0380: public RowKey getCursorRow() throws DataProviderException {
0381: return cursorRow;
0382: }
0383:
0384: /** {@inheritDoc} */
0385: public void setCursorRow(RowKey row)
0386: throws TableCursorVetoException {
0387:
0388: if (Beans.isDesignTime()) {
0389: if (!isRowAvailable(row)) {
0390: throw new IllegalArgumentException(bundle
0391: .getString("ROW_NOT_AVAILABLE")); //NOI18N
0392: }
0393: RowKey oldRow = this .cursorRow;
0394: fireCursorChanging(oldRow, row);
0395: this .cursorRow = row;
0396: fireCursorChanged(oldRow, cursorRow);
0397: return;
0398: }
0399:
0400: if (getCachedRowSet() != null
0401: && row instanceof CachedRowSetRowKey) {
0402: try {
0403: checkExecute();
0404: if (absolute(((CachedRowSetRowKey) row).getIndex() + 1)) {
0405: if (isUpdatable() && getCachedRowSet().rowDeleted()) {
0406: throw new IllegalArgumentException("" + row); //NOI18N
0407: }
0408: RowKey oldRow = this .cursorRow;
0409: fireCursorChanging(oldRow, row);
0410: this .cursorRow = row;
0411: fireCursorChanged(oldRow, cursorRow);
0412: return;
0413: } else {
0414: throw new IllegalArgumentException("" + row); //NOI18N
0415: }
0416: } catch (SQLException e) {
0417: throw new RuntimeException(e);
0418: }
0419: }
0420:
0421: }
0422:
0423: /** {@inheritDoc} */
0424: public boolean cursorFirst() throws DataProviderException {
0425:
0426: RowKey[] keys = getRowKeys(1, null);
0427: if (keys.length == 0) {
0428: return false;
0429: }
0430: try {
0431: setCursorRow((CachedRowSetRowKey) keys[0]);
0432: return true;
0433: } catch (IllegalArgumentException e) {
0434: return false;
0435: }
0436: }
0437:
0438: /** {@inheritDoc} */
0439: public boolean cursorNext() throws DataProviderException {
0440: RowKey[] keys = getRowKeys(1, getCursorRow());
0441: if (keys.length == 0) {
0442: return false;
0443: }
0444: try {
0445: setCursorRow((CachedRowSetRowKey) keys[0]);
0446: return true;
0447: } catch (IllegalArgumentException e) {
0448: return false;
0449: }
0450: }
0451:
0452: /** {@inheritDoc} */
0453: public boolean cursorPrevious() throws DataProviderException {
0454: // attempt to set cursor index until sucessfull or < 0
0455: int idx = getCursorIndex() - 1;
0456: while (idx >= 0) {
0457: try {
0458: setCursorIndex(idx);
0459: return true;
0460: } catch (IllegalArgumentException e) {
0461: }
0462: idx--;
0463: }
0464: return false;
0465: }
0466:
0467: /** {@inheritDoc} */
0468: public boolean cursorLast() throws DataProviderException {
0469: if (Beans.isDesignTime()) {
0470: setCursorIndex(2);
0471: return true;
0472: }
0473: boolean saveShowDeleted = false;
0474: try {
0475: saveShowDeleted = getCachedRowSet().getShowDeleted();
0476: getCachedRowSet().setShowDeleted(true);
0477: try {
0478: getCachedRowSet().last();
0479: do {
0480: int key = getCachedRowSet().getRow() - 1;
0481: if (!(isUpdatable() && getCachedRowSet()
0482: .rowDeleted())) {
0483: setCursorRow(new CachedRowSetRowKey(key));
0484: return true;
0485: }
0486: } while (getCachedRowSet().previous());
0487: return false;
0488: } catch (SQLException e2) {
0489: return false;
0490: }
0491: } catch (SQLException e) {
0492: return false;
0493: } finally {
0494: try {
0495: getCachedRowSet().setShowDeleted(saveShowDeleted);
0496: } catch (SQLException e) {
0497: }
0498: }
0499: }
0500:
0501: // --------------------------------------------------- DataProvider Methods
0502:
0503: /** {@inheritDoc} */
0504: public FieldKey getFieldKey(String fieldId)
0505: throws DataProviderException {
0506:
0507: /*
0508: * Bug 6275441: mssqlserver is handing back different metadata based on whether
0509: * the query has been executed or not.
0510: *
0511: */
0512: try {
0513: return getFieldKeyInternal(fieldId);
0514: } catch (IllegalArgumentException e) {
0515: /*
0516: * let's see if we hit bug #6275441
0517: */
0518: if (fieldId.indexOf('.') == -1) {
0519: // fieldId is not prepended with a table, so we may have hit 6275441
0520: if (fieldKeysMap != null) {
0521: for (Iterator i = fieldKeysMap.values().iterator(); i
0522: .hasNext();) {
0523: FieldKey fieldKey = (FieldKey) i.next();
0524: String val = fieldKey.getFieldId();
0525: int loc = val.lastIndexOf('.');
0526: if (loc >= 0 && loc + 1 < val.length()) {
0527: val = val.substring(loc + 1);
0528: }
0529: if (val.equalsIgnoreCase(fieldId)) {
0530: return fieldKey;
0531: }
0532: }
0533: }
0534: }
0535: throw e;
0536: }
0537: }
0538:
0539: private FieldKey getFieldKeyInternal(String fieldId)
0540: throws DataProviderException {
0541: if (fieldKeysMap == null) {
0542: if (fieldKeys == null) {
0543: getFieldKeys();
0544: if (fieldKeys == null) {
0545: return null;
0546: }
0547: }
0548: fieldKeysMap = new TreeMap(String.CASE_INSENSITIVE_ORDER);
0549: for (int i = 0; i < fieldKeys.length; i++) {
0550: fieldKeysMap.put(fieldKeys[i].getFieldId(),
0551: fieldKeys[i]);
0552: }
0553: }
0554: FieldKey fieldKey = (FieldKey) fieldKeysMap.get(fieldId);
0555: if (fieldKey != null) {
0556: return fieldKey;
0557: } else {
0558: throw new IllegalArgumentException(fieldId);
0559: }
0560: }
0561:
0562: /** {@inheritDoc} */
0563: public FieldKey[] getFieldKeys() throws DataProviderException {
0564: if (fieldKeys == null) {
0565: ResultSetMetaData metaData = getMetaData();
0566: if (metaData == null) {
0567: return FieldKey.EMPTY_ARRAY;
0568: }
0569: try {
0570: fieldKeys = new FieldKey[metaData.getColumnCount()];
0571: for (int i = 0; i < fieldKeys.length; i++) {
0572: String tableName = "";
0573: if (metaData.getTableName(i + 1) != null
0574: && !metaData.getTableName(i + 1).equals("")) {
0575: tableName = metaData.getTableName(i + 1) + ".";
0576: }
0577: fieldKeys[i] = new FieldKey(tableName
0578: + metaData.getColumnName(i + 1));
0579: }
0580: } catch (SQLException e) {
0581: fieldKeys = null;
0582: throw new RuntimeException(e);
0583: }
0584: }
0585: if (fieldKeys != null) {
0586: return fieldKeys;
0587: }
0588: return FieldKey.EMPTY_ARRAY;
0589: }
0590:
0591: /** {@inheritDoc} */
0592: public Class getType(FieldKey fieldKey)
0593: throws DataProviderException {
0594:
0595: ResultSetMetaData metaData = getMetaData();
0596: if (metaData != null) {
0597: try {
0598: int column = column(fieldKey);
0599: if (column > 0) {
0600: String className = metaData
0601: .getColumnClassName(column);
0602: // System.out.println("column: "+column + " className: "+className + " type: "+metaData.getColumnType(column));
0603:
0604: if (Beans.isDesignTime()
0605: && (className
0606: .equals("oracle.sql.TIMESTAMP"))) {
0607: // Special processing for non-standard Oracle classes @ design time
0608: // return Class.forName(className,
0609: // true,
0610: // getDriverClassLoader(getClass().getClassLoader()));
0611: // Return the appropriate Java class instead
0612: return java.sql.Timestamp.class;
0613: } else if (Beans.isDesignTime()
0614: && (className.equals("oracle.sql.CLOB"))) {
0615: // Convert Oracle class into standard Java class. Note that no oracle.sql.CLOB extends java.sql.Clob, so only the
0616: // type needs to be converted, not the value.
0617: return java.sql.Clob.class;
0618: } else {
0619: return Class.forName(className);
0620: }
0621: } else {
0622: throw new IllegalArgumentException(fieldKey
0623: .getFieldId());
0624: }
0625: } catch (ClassNotFoundException e) {
0626: throw new RuntimeException(e);
0627: } catch (SQLException e) {
0628: throw new RuntimeException(e);
0629: }
0630: }
0631: return null;
0632:
0633: }
0634:
0635: // ---------------------------------------------- TableDataProvider Methods
0636:
0637: /** {@inheritDoc} */
0638: public RowKey appendRow() throws DataProviderException {
0639: if (!canAppendRow()) {
0640: return null; // spec silent on what to do here
0641: }
0642: boolean saveShowDeleted = false;
0643: try {
0644: saveShowDeleted = getCachedRowSet().getShowDeleted();
0645: getCachedRowSet().setShowDeleted(true);
0646: try {
0647: getCachedRowSet().last();
0648: int newRow = getCachedRowSet().getRow(); // will be correct for new row (zero-based)
0649: getCachedRowSet().moveToInsertRow();
0650: ResultSetMetaData rsmd = getMetaData();
0651: for (int i = 1; i <= rsmd.getColumnCount(); i++) {
0652: if (rsmd.isNullable(i) == ResultSetMetaData.columnNoNulls
0653: && rsmd.isWritable(i) == true) {
0654: getCachedRowSet().updateObject(
0655: i,
0656: manufacturePlaceholder(rsmd
0657: .getColumnClassName(i), false,
0658: rsmd.getColumnType(i)));
0659: }
0660: }
0661: getCachedRowSet().insertRow();
0662: return new CachedRowSetRowKey(newRow); // zero based
0663: } catch (SQLException e2) {
0664: throw new RuntimeException(e2);
0665: }
0666: } catch (SQLException e) {
0667: throw new RuntimeException(e);
0668: } finally {
0669: try {
0670: getCachedRowSet().setShowDeleted(saveShowDeleted);
0671: } catch (SQLException e) {
0672: }
0673: }
0674: }
0675:
0676: /** {@inheritDoc} */
0677: public boolean canInsertRow(RowKey beforeRow)
0678: throws DataProviderException {
0679: return false;
0680:
0681: }
0682:
0683: /** {@inheritDoc} */
0684: public boolean canAppendRow() throws DataProviderException {
0685: try {
0686: return isUpdatable();
0687: } catch (SQLException e) {
0688: return false;
0689: }
0690: }
0691:
0692: /** {@inheritDoc} */
0693: public boolean canRemoveRow(RowKey row)
0694: throws DataProviderException {
0695: try {
0696: return isUpdatable() && isRowAvailable(row);
0697: } catch (SQLException e) {
0698: return false;
0699: }
0700: }
0701:
0702: /** {@inheritDoc} */
0703: public int getRowCount() throws DataProviderException {
0704: if (getCachedRowSet() == null) {
0705: return 0;
0706: }
0707: int rowCount = 0;
0708: int cursorIndexSave = getCursorIndex();
0709: try {
0710: int startIndex = 0;
0711: while (absolute(startIndex + 1)) {
0712: try {
0713: if (!(isUpdatable() && getCachedRowSet()
0714: .rowDeleted())) {
0715: rowCount++;
0716: }
0717: } catch (SQLException e) {
0718: return -1;
0719: }
0720: startIndex++;
0721: }
0722: return rowCount;
0723: } finally {
0724: try {
0725: setCursorIndex(cursorIndexSave);
0726: } catch (IllegalArgumentException e) {
0727: // This can happen if the row at the cursorIndex was deleted
0728: }
0729: }
0730: }
0731:
0732: /** {@inheritDoc} */
0733: public Object getValue(FieldKey fieldKey, RowKey row)
0734: throws DataProviderException {
0735:
0736: // try {
0737: // System.out.println("Entering CRSDP.getValue, fieldKey: " + fieldKey + " rowKey: " + row +
0738: // "colClassName: " + getMetaData().getColumnClassName(column(fieldKey)) +
0739: // " colType: " + getMetaData().getColumnType(column(fieldKey)));
0740: // } catch (java.sql.SQLException e) {}
0741:
0742: if (Beans.isDesignTime()) {
0743: try {
0744: return manufacturePlaceholder(getMetaData()
0745: .getColumnClassName(column(fieldKey)), true,
0746: getMetaData().getColumnType(column(fieldKey)));
0747: } catch (SQLException e) {
0748: throw new RuntimeException(e);
0749: }
0750: }
0751:
0752: if (getCachedRowSet() != null
0753: && row instanceof CachedRowSetRowKey) {
0754: try {
0755: checkExecute();
0756: if (absolute(((CachedRowSetRowKey) row).getIndex() + 1)) {
0757:
0758: Object obj = getCachedRowSet().getObject(
0759: column(fieldKey));
0760: if (getMetaData().getColumnClassName(
0761: column(fieldKey)).equals(
0762: "oracle.sql.TIMESTAMP")) {
0763: try {
0764: // If we have an Oracle TIMESTAMP object, convert to standard JDBC class
0765: // Use reflection to avoid runtime dependency
0766: Class c = Class
0767: .forName("oracle.sql.TIMESTAMP");
0768: if (obj.getClass().getName().equals(
0769: "oracle.sql.TIMESTAMP")) {
0770: Method m = c.getMethod("toJdbc",
0771: (Class[]) null);
0772: Object newObj = m.invoke(obj,
0773: (Object[]) null);
0774: return newObj;
0775: } else {
0776: return obj;
0777: }
0778: } catch (Exception e) {
0779: // ClassNotFoundException, NoSuchMethodException, IllegalAccessException
0780: return obj;
0781: }
0782:
0783: } else {
0784: return obj;
0785: }
0786: } else {
0787: throw new IndexOutOfBoundsException("" + row);
0788: }
0789: } catch (SQLException e) {
0790: throw new RuntimeException(e);
0791: }
0792: }
0793: return null;
0794:
0795: }
0796:
0797: /** {@inheritDoc} */
0798: public RowKey insertRow(RowKey beforeRow)
0799: throws DataProviderException {
0800: return null; // spec is silent on what to do here
0801:
0802: }
0803:
0804: /** {@inheritDoc} */
0805: public boolean isReadOnly(FieldKey fieldKey)
0806: throws DataProviderException {
0807:
0808: try {
0809: if (!isUpdatable()) {
0810: return true;
0811: }
0812: ResultSetMetaData metaData = getMetaData();
0813: if (metaData.isReadOnly(column(fieldKey))) {
0814: return true;
0815: }
0816: } catch (SQLException e) {
0817: return false;
0818: }
0819: return false;
0820: }
0821:
0822: /** {@inheritDoc} */
0823: public boolean isRowAvailable(RowKey row)
0824: throws DataProviderException {
0825: if (!(row instanceof CachedRowSetRowKey)) {
0826: return false;
0827: }
0828: int index = ((CachedRowSetRowKey) row).getIndex();
0829: if (Beans.isDesignTime()) {
0830: return (index >= 0) && (index <= 2);
0831: }
0832: if (getCachedRowSet() != null) {
0833: try {
0834: checkExecute();
0835: if (absolute(index + 1)) {
0836: if (isUpdatable() && getCachedRowSet().rowDeleted()) {
0837: return false;
0838: } else {
0839: return true;
0840: }
0841: }
0842: } catch (SQLException e) {
0843: return false;
0844: }
0845: }
0846: return false;
0847:
0848: }
0849:
0850: /** {@inheritDoc} */
0851: public void removeRow(RowKey row) throws DataProviderException {
0852: int index = ((CachedRowSetRowKey) row).getIndex();
0853: if (getCachedRowSet() != null) {
0854: try {
0855: if (!isUpdatable()) {
0856: throw new IllegalArgumentException("" + row); // What should we throw?
0857: }
0858: if (absolute(index + 1)) {
0859: if (!getCachedRowSet().rowDeleted()) {
0860: getCachedRowSet().deleteRow();
0861: } // FIXME: else do we throw an exception if already deleted?
0862: } else {
0863: throw new IllegalArgumentException("" + row);
0864: }
0865: } catch (SQLException e) {
0866: throw new RuntimeException(e);
0867: }
0868: } else {
0869: throw new IllegalStateException();
0870: }
0871:
0872: }
0873:
0874: /** {@inheritDoc} */
0875: public void setValue(FieldKey fieldKey, RowKey row, Object value)
0876: throws DataProviderException {
0877:
0878: if (getCachedRowSet() != null
0879: && row instanceof CachedRowSetRowKey) {
0880: try {
0881: if (!isUpdatable()) {
0882: throw new IllegalStateException(""
0883: + fieldKey.getFieldId());
0884: }
0885: if (absolute(((CachedRowSetRowKey) row).getIndex() + 1)) {
0886: if (getCachedRowSet().rowDeleted()) {
0887: throw new IllegalStateException(""
0888: + fieldKey.getFieldId());
0889: }
0890: if (isReadOnly(fieldKey)) {
0891: throw new IllegalStateException(""
0892: + fieldKey.getFieldId());
0893: }
0894: int column = column(fieldKey);
0895:
0896: // if (getMetaData().getColumnClassName(column(fieldKey)).equals("oracle.sql.TIMESTAMP")) {
0897: // // Extra code to create oracle.sql.TIMESTAMP objects from java.sql.Timestamp
0898: // // May not be required
0899: // }
0900:
0901: Object old = getCachedRowSet().getObject(column);
0902: boolean changed = false;
0903: if (old == null) {
0904: changed = value != null;
0905: } else if (value == null) {
0906: changed = true;
0907: } else {
0908: changed = !old.equals(value);
0909: }
0910:
0911: if (changed) {
0912: getCachedRowSet().updateObject(column, value);
0913: getCachedRowSet().updateRow();
0914: fireValueChanged(fieldKey, row, old, value);
0915: if (row != null && row.equals(getCursorRow())) {
0916: fireValueChanged(fieldKey, old, value);
0917: }
0918: }
0919: } else {
0920: throw new IndexOutOfBoundsException("" + row);
0921: }
0922: } catch (SQLException e) {
0923: throw new RuntimeException(e);
0924: }
0925: }
0926:
0927: }
0928:
0929: // -------------------------------------- RefreshableDataProvider Methods
0930:
0931: /** {@inheritDoc} */
0932: public void addRefreshableDataListener(
0933: RefreshableDataListener listener) {
0934: super .addDataListener(listener);
0935: }
0936:
0937: /** {@inheritDoc} */
0938: public void removeRefreshableDataListener(
0939: RefreshableDataListener listener) {
0940: super .removeDataListener(listener);
0941: }
0942:
0943: /** {@inheritDoc} */
0944: public RefreshableDataListener[] getRefreshableDataListeners() {
0945: if (dpListeners == null) {
0946: return new RefreshableDataListener[0];
0947: } else {
0948: ArrayList rdList = new ArrayList();
0949: for (int i = 0; i < dpListeners.length; i++) {
0950: if (dpListeners[i] instanceof RefreshableDataListener) {
0951: rdList.add(dpListeners[i]);
0952: }
0953: }
0954: return (RefreshableDataListener[]) rdList
0955: .toArray(new RefreshableDataListener[rdList.size()]);
0956: }
0957: }
0958:
0959: /** {@inheritDoc} */
0960: public void refresh() throws DataProviderException {
0961: if (getCachedRowSet() != null
0962: && getCachedRowSet() instanceof CachedRowSet) {
0963: try {
0964: ((CachedRowSet) getCachedRowSet()).release();
0965: executed = false; // reset -executed- so refetch occurs
0966: fireRefreshed();
0967: } catch (SQLException e) {
0968: throw new RuntimeException(e);
0969: }
0970: }
0971: }
0972:
0973: // -------------------------------------- TransactionalDataProvider Methods
0974:
0975: /** {@inheritDoc} */
0976: public void commitChanges() throws DataProviderException {
0977: if (getCachedRowSet() != null) {
0978: try {
0979: checkExecute();
0980: try {
0981: getCachedRowSet().acceptChanges();
0982: fireChangesCommitted();
0983: } catch (SyncProviderException spe) {
0984: SyncResolver resolver = spe.getSyncResolver();
0985: String message = null;
0986: if (resolver == null) {
0987: message = spe.getMessage();
0988: } else {
0989: while (resolver.nextConflict()) {
0990: int row = resolver.getRow();
0991:
0992: //TODO: use MessageFormat for these error strings
0993: String pattern = "{0} {1} {2}"; // NOI18N
0994: switch (resolver.getStatus()) {
0995: case SyncResolver.DELETE_ROW_CONFLICT:
0996: pattern = bundle
0997: .getString("DELETE_ROW_CONFLICT"); //NOI18N
0998: break;
0999: case SyncResolver.INSERT_ROW_CONFLICT:
1000: pattern = bundle
1001: .getString("INSERT_ROW_CONFLICT"); // NOI18N
1002: break;
1003: case SyncResolver.UPDATE_ROW_CONFLICT:
1004: pattern = bundle
1005: .getString("UPDATE_ROW_CONFLICT"); // NOI18N
1006: break;
1007: }
1008: String sqlExceptionText = "";
1009: if (resolver instanceof SyncResolverX) {
1010: sqlExceptionText = ((SyncResolverX) resolver)
1011: .getSQLException()
1012: .getLocalizedMessage();
1013: }
1014: String[] args = new String[] {
1015: spe.getMessage(),
1016: Integer.toString(row - 1),
1017: sqlExceptionText };
1018: message = MessageFormat.format(pattern,
1019: args);
1020: absolute(row);
1021: int colCount = getCachedRowSet()
1022: .getMetaData().getColumnCount();
1023: for (int i = 1; i <= colCount; i++) {
1024: try {
1025: if (resolver.getConflictValue(i) != null) {
1026: message += ": "
1027: + resolver
1028: .getConflictValue( //NOI18N
1029: getCachedRowSet()
1030: .getMetaData()
1031: .getColumnName(
1032: i));
1033: }
1034: } catch (SQLException se) {
1035: // should never be here.
1036: message += ": <unknown>"; // NOI18N
1037: }
1038: }
1039: }
1040: }
1041: throw new RuntimeException(message, spe);
1042: }
1043: } catch (SQLException sqle) {
1044: throw new RuntimeException(sqle);
1045:
1046: }
1047: }
1048: }
1049:
1050: /** {@inheritDoc} */
1051: public void revertChanges() throws DataProviderException {
1052: if (getCachedRowSet() != null) {
1053: try {
1054: checkExecute();
1055: ((CachedRowSet) getCachedRowSet()).restoreOriginal();
1056: fireChangesReverted();
1057: } catch (SQLException e) {
1058: throw new RuntimeException(e);
1059: }
1060: }
1061: }
1062:
1063: /** {@inheritDoc} */
1064: public void addTransactionalDataListener(
1065: TransactionalDataListener listener) {
1066: super .addDataListener(listener);
1067: }
1068:
1069: /** {@inheritDoc} */
1070: public void removeTransactionalDataListener(
1071: TransactionalDataListener listener) {
1072: super .removeDataListener(listener);
1073: }
1074:
1075: /** {@inheritDoc} */
1076: public TransactionalDataListener[] getTransactionalDataListeners() {
1077: if (dpListeners == null) {
1078: return new TransactionalDataListener[0];
1079: } else {
1080: ArrayList tdList = new ArrayList();
1081: for (int i = 0; i < dpListeners.length; i++) {
1082: if (dpListeners[i] instanceof TransactionalDataListener) {
1083: tdList.add(dpListeners[i]);
1084: }
1085: }
1086: return (TransactionalDataListener[]) tdList
1087: .toArray(new TransactionalDataListener[tdList
1088: .size()]);
1089: }
1090: }
1091:
1092: // -------------------------------------------------------- Private Classes
1093:
1094: /**
1095: * <p>CachedRowSetDataProvider's private RowKey class
1096: */
1097: static private class CachedRowSetRowKey extends RowKey implements
1098: Serializable {
1099:
1100: /**
1101: * Constructs a new CachedRowSetRowKey from the passed rowId String, or returns
1102: * null if the passed rowId cannot be parsed into an int index.
1103: *
1104: * @param rowId The canonical row ID string to parse into an int
1105: * @return An CachedRowSetRowKey representing the passed rowId, or null if one
1106: * could not be created.
1107: */
1108: public static CachedRowSetRowKey create(String rowId) {
1109: try {
1110: return new CachedRowSetRowKey(Integer.parseInt(rowId));
1111: } catch (NumberFormatException nfx) {
1112: return null;
1113: }
1114: }
1115:
1116: /**
1117: * Constructs an CachedRowSetRowKey using the specified index
1118: *
1119: * @param index The desired index
1120: */
1121: CachedRowSetRowKey(int index) {
1122: super (String.valueOf(index));
1123: this .index = index;
1124: }
1125:
1126: /**
1127: * Returns the index of this CachedRowSetRowKey
1128: *
1129: * @return This CachedRowSetRowKey's index value
1130: */
1131: int getIndex() {
1132: return index;
1133: }
1134:
1135: /**
1136: * Standard equals implementation. This method compares the CachedRowSetRowKey
1137: * index values for == equality. If the passed Object is not an
1138: * CachedRowSetRowKey instance, the superclass (RowKey) gets a chance to evaluate
1139: * the Object for equality.
1140: *
1141: * @param o the Object to check equality
1142: * @return true if equal, false if not
1143: * @see Object#equals(Object)
1144: */
1145: public boolean equals(Object o) {
1146: if (o instanceof CachedRowSetRowKey) {
1147: return ((CachedRowSetRowKey) o).getIndex() == getIndex();
1148: }
1149: return super .equals(o);
1150: }
1151:
1152: private int index;
1153:
1154: /**
1155: * <p>Return a printable version of this instance.</p>
1156: */
1157: public String toString() {
1158: return "CachedRowSetRowKey[" + index + "]";
1159: }
1160: }
1161:
1162: static private class ColumnNotSet implements Serializable {
1163: public String toString() {
1164: return null;
1165: }
1166: }
1167:
1168: private class RowSetPropertyChangeListener implements
1169: PropertyChangeListener, Serializable {
1170: public void propertyChange(PropertyChangeEvent event) {
1171: if (event.getPropertyName().equals("command") || //NOI18N
1172: event.getPropertyName().equals("dataSourceName") || //NOI18N
1173: event.getPropertyName().equals("url") || //NOI18N
1174: event.getPropertyName().equals("username")) { //NOI18N
1175: try {
1176: setCursorIndex(0);
1177: } catch (Exception e) {
1178: }
1179: metaData = null;
1180: fieldKeys = null;
1181: fieldKeysMap = null;
1182: executed = false;
1183: refreshMetaDataFile = true;
1184: fireProviderChanged();
1185: }
1186: }
1187: }
1188:
1189: private class CachedRowSetListener implements RowSetListener,
1190: Serializable {
1191: public void cursorMoved(RowSetEvent event) {
1192: }
1193:
1194: public void rowChanged(RowSetEvent event) {
1195: try {
1196: int row = ((RowSet) event.getSource()).getRow();
1197: //Don't know what column changed
1198: //fireValueChanged(fieldKey, new CachedRowSetRowKey(row+1), old, value);
1199: } catch (SQLException e) {
1200: //FIXME: What to do?
1201: }
1202: }
1203:
1204: public void rowSetChanged(RowSetEvent event) {
1205: fireProviderChanged();
1206: }
1207: }
1208:
1209: // -------------------------------------------------------- Private Methods
1210:
1211: private void fireRefreshed() {
1212: RefreshableDataListener[] rdList = getRefreshableDataListeners();
1213: for (int i = 0; i < rdList.length; i++) {
1214: rdList[i].refreshed(this );
1215: }
1216: }
1217:
1218: /**
1219: * <p>Manufacture an empty (or as close to empty as we can get) instance
1220: * of class className (to be used a placeholder when inserting a row)</p>
1221: */
1222: static private Object manufacturePlaceholder(String className,
1223: boolean fakeData, int columnType) throws SQLException {
1224: // System.out.println("Entering manufacturePlaceholder, className: "+className + " columnType: "+columnType+" fakeData: "+fakeData);
1225: /*
1226: * for classes we know don't have null constructors, we'll create them explicitly.
1227: */
1228: if (className.equals("java.sql.Date")) { // NOI18N
1229: return new Date(new java.util.Date().getTime());
1230: } else if (className.equals("java.sql.Time")) { // NOI18N
1231: return new Time(new java.util.Date().getTime());
1232: } else if (className.equals("java.sql.Timestamp")) { // NOI18N
1233: return new Timestamp(new java.util.Date().getTime());
1234: } else if (className.equals("java.math.BigDecimal")) { // NOI18N
1235: return new BigDecimal(fakeData ? BigInteger.ONE
1236: : BigInteger.ZERO);
1237: } else if (className.equals("java.math.BigInteger")) { // NOI18N
1238: return fakeData ? BigInteger.ONE : BigInteger.ZERO;
1239: } else if (className.equals("java.lang.Boolean")) { // NOI18N
1240: return fakeData ? Boolean.TRUE : Boolean.FALSE;
1241: } else if (className.equals("java.lang.Byte")) { // NOI18N
1242: return new Byte(fakeData ? (byte) 123 : (byte) 0);
1243: } else if (className.equals("java.lang.Character")) { // NOI18N
1244: return new Character(fakeData ? 'c' : ' '); // NOI18N
1245: } else if (className.equals("java.lang.Double")) { // NOI18N
1246: return new Double(fakeData ? 123.0 : 0.0);
1247: } else if (className.equals("java.lang.Float")) { // NOI18N
1248: return new Float(fakeData ? 123.0 : 0.0);
1249: } else if (className.equals("java.lang.Integer")) { // NOI18N
1250: return new Integer(fakeData ? 123 : 0);
1251: } else if (className.equals("java.lang.Long")) { // NOI18N
1252: return new Long(fakeData ? 123 : 0);
1253: } else if (className.equals("java.lang.Short")) { // NOI18N
1254: return new Short((short) (fakeData ? 123 : 0));
1255: } else if (className.equals("java.lang.String")) { // NOI18N
1256: return fakeData ? bundle.getString("arbitraryCharData")
1257: : ""; //NOI18N
1258: } else if (className.equals("java.sql.Blob")) { // NOI18N
1259: try {
1260: return new javax.sql.rowset.serial.SerialBlob(
1261: fakeData ? (new byte[] { 1, 2, 3, 4, 5 })
1262: : (new byte[0]));
1263: } catch (SQLException e) {
1264: return new Object();
1265: }
1266: } else if (className.equals("javax.sql.SerialClob")) { // NOI18N
1267: try {
1268: return new javax.sql.rowset.serial.SerialClob(
1269: fakeData ? (bundle
1270: .getString("arbitraryClobData")
1271: .toCharArray()) : //NOI18N
1272: new char[0]);
1273: } catch (SQLException e) {
1274: return new Object();
1275: }
1276: } else if (className.equals("java.net.URL")) { // NOI18N
1277: try {
1278: if (fakeData) {
1279: return new java.net.URL("http://www.sun.com"); //NOI18N
1280: } else {
1281: return new java.net.URL(""); // NOI18N
1282: }
1283: } catch (java.net.MalformedURLException e) {
1284: return new Object();
1285: }
1286: } else if (className.equals("java.sql.Array")) { // NOI18N
1287: return new java.sql.Array() {
1288: public Object getArray() {
1289: return null;
1290: }
1291:
1292: public Object getArray(long index, int count) {
1293: return null;
1294: }
1295:
1296: public Object getArray(long index, int count, Map map) {
1297: return null;
1298: }
1299:
1300: public Object getArray(Map map) {
1301: return null;
1302: }
1303:
1304: public int getBaseType() {
1305: return Types.CHAR;
1306: }
1307:
1308: public String getBaseTypeName() {
1309: return "CHAR"; //NOI18N
1310: }
1311:
1312: public ResultSet getResultSet() {
1313: return null;
1314: }
1315:
1316: public ResultSet getResultSet(long index, int count) {
1317: return null;
1318: }
1319:
1320: public ResultSet getResultSet(long index, int count,
1321: Map map) {
1322: return null;
1323: }
1324:
1325: public ResultSet getResultSet(Map map) {
1326: return null;
1327: }
1328:
1329: public void free() {
1330: }
1331: };
1332: } else if (className.equals("char[]")) { // NOI18N
1333: return fakeData ? new char[] { 'a', 'b', 'c', 'd', 'e' }
1334: : new char[0];
1335: } else if (className.equals("byte[]")) { // NOI18N
1336: return fakeData ? new byte[] { 1, 2, 3, 4, 5 }
1337: : new byte[0];
1338: } else if (className.equals("java.sql.Ref")) { // NOI18N
1339: return new java.sql.Ref() {
1340: private Object data = bundle
1341: .getString("arbitraryCharData"); //NOI18N
1342:
1343: public String getBaseTypeName() {
1344: return "CHAR"; //NOI18N
1345: }
1346:
1347: public Object getObject() {
1348: return data;
1349: }
1350:
1351: public Object getObject(Map map) {
1352: return data;
1353: }
1354:
1355: public void setObject(Object value) {
1356: data = value;
1357: }
1358: };
1359: } else if (className.equals("java.sql.Struct")) { // NOI18N
1360: return new java.sql.Struct() {
1361: private String[] data = {
1362: bundle.getString("arbitraryCharData"), //NOI18N
1363: bundle.getString("arbitraryCharData2"), //NOI18N
1364: bundle.getString("arbitraryCharData3") }; //NOI18N
1365:
1366: public Object[] getAttributes() {
1367: return data;
1368: }
1369:
1370: public Object[] getAttributes(Map map) {
1371: return data;
1372: }
1373:
1374: public String getSQLTypeName() {
1375: return "CHAR"; //NOI18N
1376: }
1377: };
1378: }
1379:
1380: if (fakeData) {
1381: // At least for now, let's try to use the old getFakeData
1382: //System.out.println("JK: temporary: fell through to use getFakeData()");
1383: return getFakeData(columnType);
1384: }
1385:
1386: try {
1387: return Class.forName(className).newInstance();
1388: } catch (ClassNotFoundException e) {
1389: return new ColumnNotSet();
1390: } catch (InstantiationException e) {
1391: return new ColumnNotSet();
1392: } catch (IllegalAccessException e) {
1393: return new ColumnNotSet();
1394: }
1395: }
1396:
1397: /**
1398: * <p>Check if rowset, if so, execute if necessary.</p>
1399: */
1400: protected void checkExecute() throws SQLException {
1401: if (!Beans.isDesignTime() && !executed) {
1402: getCachedRowSet().execute();
1403: getCachedRowSet().first();
1404: executed = true;
1405: }
1406: }
1407:
1408: /**
1409: * <p>Check whether resultset is updatable or not.</p>
1410: */
1411: private boolean isUpdatable() throws SQLException {
1412: if (Beans.isDesignTime() || getCachedRowSet() == null) {
1413: return false;
1414: }
1415: checkExecute();
1416: if (getCachedRowSet().getConcurrency() == ResultSet.CONCUR_READ_ONLY) {
1417: return false;
1418: } else {
1419: return true;
1420: }
1421: }
1422:
1423: /**
1424: * <p>Return the one-relative column number corresponding to the
1425: * specified {@link FieldKey}, or zero if there is no such column
1426: * available.</p>
1427: *
1428: * @param fieldKey {@link FieldKey} representing this column
1429: */
1430: private int column(FieldKey fieldKey) throws DataProviderException {
1431:
1432: if (fieldKeys == null) {
1433: getFieldKeys();
1434: }
1435: if (fieldKeys != null) {
1436: for (int i = 0; i < fieldKeys.length; i++) {
1437: if (fieldKey.getFieldId().equals(
1438: fieldKeys[i].getFieldId())) {
1439: return i + 1;
1440: }
1441: }
1442: /*
1443: * Here we are also matching on fieldKey,
1444: * so bug #6275441 workaround applies here too.
1445: * let's see if we hit bug #6275441
1446: */
1447: if (fieldKey.getFieldId().indexOf('.') == -1) {
1448: // fieldId is not prepended with a table (and schema),
1449: // so we may have hit 6275441
1450: for (int i = 0; i < fieldKeys.length; i++) {
1451: String val = fieldKeys[i].getFieldId();
1452: int loc = val.lastIndexOf('.');
1453: if (loc >= 0 && loc + 1 < val.length()) {
1454: val = val.substring(loc + 1);
1455: }
1456: if (val.equalsIgnoreCase(fieldKey.getFieldId())) {
1457: return i + 1;
1458: }
1459: }
1460: }
1461: }
1462: return 0;
1463: }
1464:
1465: /**
1466: * <p>Fires a changesCommtted event to each registered {@link
1467: * TransactionalDataListener}.</p>
1468: */
1469: protected void fireChangesCommitted() {
1470: TransactionalDataListener[] tdList = getTransactionalDataListeners();
1471: for (int i = 0; i < tdList.length; i++) {
1472: tdList[i].changesCommitted(this );
1473: }
1474: }
1475:
1476: /**
1477: * <p>Fires a changesReverted event to each registered {@link
1478: * TransactionalDataListener}.</p>
1479: */
1480: protected void fireChangesReverted() {
1481: TransactionalDataListener[] tdList = getTransactionalDataListeners();
1482: for (int i = 0; i < tdList.length; i++) {
1483: tdList[i].changesReverted(this );
1484: }
1485: }
1486:
1487: /**
1488: * <p>Return the <code>ResultSetMetaData</code> instance for the
1489: * <code>ResultSet</code> we are wrapping, caching it the first
1490: * time it is requested.</p>
1491: */
1492: private ResultSetMetaData getMetaData() {
1493:
1494: if (metaData == null) {
1495: if (getCachedRowSet() != null) {
1496: try {
1497: // Generate filename for serialized result set metadata
1498: MetaDataSerializer mdSerializer = new MetaDataSerializer();
1499: String filename = mdSerializer
1500: .generateMetaDataName(generateFilename());
1501:
1502: // the filename path begins with NetBeans userdir which is underdetermined at runtime
1503: if (filename.startsWith("null")) { // NOI18N
1504: metaData = getCachedRowSet().getMetaData();
1505: } else {
1506: // if it's the first time using a rowset then need to get live data prior to serializing
1507: if (!mdSerializer.mdFileNameExists(filename)) {
1508: metaData = getCachedRowSet().getMetaData();
1509: mdSerializer.serialize(metaData, filename);
1510: metaDataFilename = filename;
1511: } else {
1512: // create metadata file if refresh flag is true
1513: if (refreshMetaDataFile) {
1514: if (metaDataFilename != null) {
1515: File metaDataFile = new File(
1516: metaDataFilename);
1517: if (metaDataFile.exists()) {
1518: metaDataFile.delete();
1519: }
1520: }
1521:
1522: metaDataFilename = filename;
1523: metaData = getCachedRowSet()
1524: .getMetaData();
1525: mdSerializer.serialize(metaData,
1526: filename);
1527: refreshMetaDataFile = false;
1528: } else {
1529: // Deserialize file now that it is available
1530: metaData = new MetaDataDeserializer()
1531: .deserialize(filename);
1532: }
1533: }
1534: }
1535: } catch (SQLException e) {
1536: throw new RuntimeException(e);
1537: }
1538: }
1539: }
1540:
1541: return metaData;
1542: }
1543:
1544: private static URLClassLoader getDriverClassLoader(
1545: ClassLoader parent) {
1546:
1547: File libDir = new File(System.getProperty("netbeans.user"),
1548: "jdbc-drivers"); // NOI18N
1549:
1550: File[] files = libDir.listFiles(new FileFilter() {
1551: public boolean accept(File f) {
1552: return f.isDirectory()
1553: || f.getName().toLowerCase().endsWith("jar") // NOI18N
1554: || f.getName().toLowerCase().endsWith("zip"); // NOI18N
1555: }
1556: });
1557:
1558: int len = (files == null) ? 0 : files.length;
1559:
1560: URL[] urls = new URL[len];
1561:
1562: for (int i = 0; i < len; i++) {
1563: try {
1564: urls[i] = files[i].toURL();
1565: } catch (MalformedURLException e) {
1566: // ignore
1567: }
1568: }
1569: return URLClassLoader.newInstance(urls, parent);
1570: }
1571:
1572: // --------------------------------------------- Design Time Related Methods
1573:
1574: /**
1575: * <p>Return design time data of a type corresponding to that of the
1576: * specified column.</p>
1577: *
1578: * @param rsmd <code>ResultSetMetaData</code> for this result set
1579: * @param columnName Name of the requested column
1580: */
1581: private static Object getFakeData(int columnType)
1582: throws SQLException {
1583:
1584: switch (columnType) {
1585:
1586: case Types.ARRAY:
1587: return new java.sql.Array() {
1588: public Object getArray() {
1589: return null;
1590: }
1591:
1592: public Object getArray(long index, int count) {
1593: return null;
1594: }
1595:
1596: public Object getArray(long index, int count, Map map) {
1597: return null;
1598: }
1599:
1600: public Object getArray(Map map) {
1601: return null;
1602: }
1603:
1604: public int getBaseType() {
1605: return Types.CHAR;
1606: }
1607:
1608: public String getBaseTypeName() {
1609: return "CHAR"; //NOI18N
1610: }
1611:
1612: public ResultSet getResultSet() {
1613: return null;
1614: }
1615:
1616: public ResultSet getResultSet(long index, int count) {
1617: return null;
1618: }
1619:
1620: public ResultSet getResultSet(long index, int count,
1621: Map map) {
1622: return null;
1623: }
1624:
1625: public ResultSet getResultSet(Map map) {
1626: return null;
1627: }
1628:
1629: public void free() {
1630: }
1631: };
1632:
1633: case Types.BIGINT:
1634: return new Long(123);
1635:
1636: case Types.BINARY:
1637: return new byte[] { 1, 2, 3, 4, 5 };
1638:
1639: case Types.BIT:
1640: return new Boolean(true);
1641:
1642: case Types.BLOB:
1643: return new javax.sql.rowset.serial.SerialBlob(new byte[] {
1644: 1, 2, 3, 4, 5 });
1645:
1646: case Types.BOOLEAN:
1647: return new Boolean(true);
1648:
1649: case Types.CHAR:
1650: return bundle.getString("arbitraryCharData"); //NOI18N
1651:
1652: case Types.CLOB:
1653: return new javax.sql.rowset.serial.SerialClob(bundle
1654: .getString("arbitraryClobData").toCharArray()); //NOI18N
1655:
1656: case Types.DATALINK:
1657: try {
1658: return new java.net.URL("http://www.sun.com"); //NOI18N
1659: } catch (java.net.MalformedURLException e) {
1660: return null;
1661: }
1662:
1663: case Types.DATE:
1664: return new java.sql.Date(new java.util.Date().getTime());
1665:
1666: case Types.DECIMAL:
1667: return new java.math.BigDecimal(java.math.BigInteger.ONE);
1668:
1669: case Types.DISTINCT:
1670: return null;
1671:
1672: case Types.DOUBLE:
1673: return new Double(123);
1674:
1675: case Types.FLOAT:
1676: return new Double(123);
1677:
1678: case Types.INTEGER:
1679: return new Integer(123);
1680:
1681: case Types.JAVA_OBJECT:
1682: return bundle.getString("arbitraryCharData"); //NOI18N
1683:
1684: case Types.LONGVARBINARY:
1685: return new byte[] { 1, 2, 3, 4, 5 };
1686:
1687: case Types.LONGVARCHAR:
1688: return bundle.getString("arbitraryCharData"); //NOI18N
1689:
1690: case Types.NULL:
1691: return null;
1692:
1693: case Types.NUMERIC:
1694: return new java.math.BigDecimal(java.math.BigInteger.ONE);
1695:
1696: case Types.OTHER:
1697: return null;
1698:
1699: case Types.REAL:
1700: return new Float(123);
1701:
1702: case Types.REF:
1703: return new java.sql.Ref() {
1704:
1705: private Object data = bundle
1706: .getString("arbitraryCharData"); //NOI18N
1707:
1708: public String getBaseTypeName() {
1709: return "CHAR"; //NOI18N
1710: }
1711:
1712: public Object getObject() {
1713: return data;
1714: }
1715:
1716: public Object getObject(Map map) {
1717: return data;
1718: }
1719:
1720: public void setObject(Object value) {
1721: data = value;
1722: }
1723: };
1724: case Types.SMALLINT:
1725: return new Short((short) 123);
1726:
1727: case Types.STRUCT:
1728: return new java.sql.Struct() {
1729:
1730: private String[] data = {
1731: bundle.getString("arbitraryCharData"), //NOI18N
1732: bundle.getString("arbitraryCharData2"), //NOI18N
1733: bundle.getString("arbitraryCharData3") }; //NOI18N
1734:
1735: public Object[] getAttributes() {
1736: return data;
1737: }
1738:
1739: public Object[] getAttributes(Map map) {
1740: return data;
1741: }
1742:
1743: public String getSQLTypeName() {
1744: return "CHAR"; //NOI18N
1745: }
1746: };
1747:
1748: case Types.TIME:
1749: return new java.sql.Time(new java.util.Date().getTime());
1750:
1751: case Types.TIMESTAMP:
1752: return new java.sql.Timestamp(new java.util.Date()
1753: .getTime());
1754:
1755: case Types.TINYINT:
1756: return new Byte((byte) 123);
1757:
1758: case Types.VARBINARY:
1759: return new byte[] { 1, 2, 3, 4, 5 };
1760:
1761: case Types.VARCHAR:
1762: return bundle.getString("arbitraryCharData"); //NOI18N
1763:
1764: default:
1765: return null;
1766:
1767: }
1768:
1769: }
1770:
1771: private String generateFilename() {
1772: // Serialize ResultSetMetaData for improved design-time performance
1773:
1774: String dataSourceName = getCachedRowSet().getDataSourceName()
1775: .replaceFirst("java:comp/env/jdbc/", ""); // NOI18N
1776: String commandName = getCachedRowSet().getCommand().replaceAll(
1777: " ", "").replaceAll("\\p{Punct}+", ""); // NOI18N
1778: commandName = commandName.toLowerCase();
1779: commandName = commandName.replaceFirst("selectfrom", ""); // NOI18N
1780: commandName = commandName.replaceFirst("selectall", ""); // NOI18N
1781:
1782: if (commandName.length() > 200) {
1783: commandName = commandName.substring(0, 200);
1784: }
1785:
1786: return dataSourceName + "_" + "_" + commandName; // NOI18N
1787: }
1788: }
|