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.ui.wizard;
042:
043: import java.sql.Types;
044: import java.util.ArrayList;
045: import java.util.Arrays;
046: import java.util.Collection;
047: import java.util.Collections;
048: import java.util.Iterator;
049: import java.util.List;
050:
051: import org.axiondb.DataType;
052: import org.axiondb.types.BigDecimalType;
053: import org.axiondb.types.TimeType;
054: import org.axiondb.types.TimestampType;
055: import org.netbeans.modules.mashup.db.common.SQLUtils;
056: import org.netbeans.modules.mashup.db.model.FlatfileDBColumn;
057: import org.netbeans.modules.mashup.db.model.FlatfileDBTable;
058:
059: import com.sun.sql.framework.utils.StringUtil;
060: import net.java.hulp.i18n.Logger;
061: import org.netbeans.modules.etl.logger.Localizer;
062: import org.netbeans.modules.etl.logger.LogUtil;
063:
064: /**
065: * @author Jonathan Giron
066: * @author Ahimanikya Satapathy
067: * @version $Revision$
068: */
069: public class FlatfileColumnTableModel extends RowEntryTableModel {
070:
071: private static transient final Logger mLogger = LogUtil
072: .getLogger(FlatfileColumnTableModel.class.getName());
073:
074: private static transient final Localizer mLoc = Localizer.get();
075:
076: /**
077: * Implementation of RowEntry interface that wraps around the content of a
078: * FlatfileDBColumn instance.
079: */
080: public static class ColumnEntry implements
081: RowEntryTableModel.RowEntry {
082: /* column data model */
083: private FlatfileDBColumn column;
084:
085: /**
086: * Creates an instance of ColumnEntry that represents the given flatfile column
087: * and exists within a flatfile of the given type.
088: *
089: * @param aColumn FlatfileDBColumn that this RowEntry will represent
090: */
091: public ColumnEntry(FlatfileDBColumn aColumn) {
092: if (aColumn == null) {
093: throw new IllegalArgumentException(
094: "Must supply non-null FlatfileDBColumn reference.");
095: }
096:
097: column = aColumn;
098: }
099:
100: /**
101: * Overrides default implementation.
102: *
103: * @param o Object to be compared against this for equality
104: * @return true if o is functionally equivalent to this, false otherwise
105: */
106: public boolean equals(Object o) {
107: if (o == this ) {
108: return true;
109: } else if (o == null) {
110: return false;
111: }
112:
113: ColumnEntry anEntry = (ColumnEntry) o;
114: return (column != null) ? column.equals(anEntry.column)
115: : (anEntry.column == null);
116: }
117:
118: /**
119: * Gets reference to underlying FlatfileDBColumn instance.
120: *
121: * @return FlatfileDBColumn that this RowEntry represents
122: */
123: public FlatfileDBColumn getColumn() {
124: return column;
125: }
126:
127: /**
128: * Returns List of Strings describing errors, if any, associated with this
129: * RowEntry.
130: *
131: * @return List, possibly empty, of errors associated with the contents of this
132: * RowEntry
133: */
134: public List validateColumnDefinition() {
135: List errorList = new ArrayList();
136:
137: String columnName = column.getName();
138: String nbBundle1 = mLoc
139: .t("PRSR001: Missing column name - please enter a unique string value.");
140: String nbBundle2 = mLoc
141: .t("PRSR001: Invalid column name - must start with an alphabetical character and contain alphanumeric characters and/or underscores.");
142: String nbBundle3 = mLoc
143: .t("PRSR001: Invalid length - please enter a non-zero, positive integer value.");
144: if (columnName == null || columnName.trim().length() == 0) {
145: String msg = Localizer.parse(nbBundle1);
146: errorList.add(msg);
147: } else if (!StringUtil.isValid(columnName,
148: "[A-Za-z]+[A-Za-z0-9_$#]*")) {
149: String msg = Localizer.parse(nbBundle2);
150: errorList.add(msg);
151: }
152:
153: int precLength = column.getPrecision();
154: if (precLength <= 0) {
155: String msg = Localizer.parse(nbBundle3);
156: errorList.add(msg);
157: }
158:
159: int sqlType = Integer.MIN_VALUE;
160: String nbBundle4 = mLoc
161: .t("PRSR001: Invalid data type - please select from the list of available types.");
162: if (column.getJdbcTypeString() == null) {
163: String msg = Localizer.parse(nbBundle4);
164: errorList.add(msg);
165: } else {
166: sqlType = column.getJdbcType();
167: }
168:
169: int scale = column.getScale();
170: String nbBundle5 = mLoc
171: .t("PRSR001: Invalid scale - please enter a non-negative integer value.");
172: String nbBundle6 = mLoc
173: .t("PRSR001: Scale exceeds precision - please enter a smaller non-negative integer value.");
174: if (column.getScale() < 0) {
175: String msg = Localizer.parse(nbBundle5);
176: errorList.add(msg);
177: } else if (Types.NUMERIC == sqlType && scale > precLength) {
178: String msg = Localizer.parse(nbBundle6);
179: errorList.add(msg);
180: }
181: String nbBundle7 = mLoc.t("PRSR001: Column #{0}:",
182: new Integer(column.getOrdinalPosition()));
183: if (!errorList.isEmpty()) {
184: String header = Localizer.parse(nbBundle7);
185: errorList.add(0, header);
186: }
187:
188: if (column.getJdbcType() == Types.NUMERIC) {
189: try {
190: DataType type = new BigDecimalType();
191: type.convert(column.getDefaultValue());
192: } catch (Exception e) {
193: errorList
194: .add("Invalid default value for numeric type");
195: }
196: }
197:
198: if (column.getJdbcType() == Types.TIMESTAMP) {
199: try {
200: DataType type = new TimestampType();
201: type.convert(column.getDefaultValue());
202: } catch (Exception e) {
203: errorList
204: .add("Invalid default value for timestamp type");
205: }
206: }
207:
208: if (column.getJdbcType() == Types.TIME) {
209: try {
210: DataType type = new TimeType();
211: type.convert(column.getDefaultValue());
212: } catch (Exception e) {
213: errorList
214: .add("Invalid default value for time type");
215: }
216: }
217:
218: return errorList;
219: }
220:
221: /**
222: * Gets name of the flatfile column underlying this RowEntry.
223: *
224: * @return column name
225: */
226: public String getName() {
227: return column.getName();
228: }
229:
230: /**
231: * @see org.netbeans.modules.mashup.db.ui.wizard.RowEntryTableModel.RowEntry#getValue
232: */
233: public Object getValue(int index) {
234: switch (index) {
235: case COLUMN_ID:
236: return new Integer(column.getCardinalPosition());
237:
238: case COLUMN_PRECLENGTH:
239: return new Integer(column.getPrecision());
240:
241: case COLUMN_NAME:
242: return column.getName();
243:
244: case COLUMN_JDBCTYPE:
245: return column.getJdbcTypeString();
246:
247: case COLUMN_SCALE:
248: if (isEditable(index)) {
249: return new Integer(column.getScale());
250: }
251: return "";
252:
253: case COLUMN_ISNULLABLE:
254: return Boolean.valueOf(column.isNullable());
255:
256: case COLUMN_ISPK:
257: return Boolean.valueOf(column.isPrimaryKey());
258:
259: case COLUMN_DAFAULT:
260: return column.getDefaultValue();
261:
262: default:
263: throw new IndexOutOfBoundsException();
264: }
265: }
266:
267: /**
268: * Overrides default implementation.
269: *
270: * @return computed hash code
271: */
272: public int hashCode() {
273: return column.hashCode();
274: }
275:
276: /**
277: * @see org.netbeans.modules.mashup.db.ui.wizard.RowEntryTableModel.RowEntry#isEditable
278: */
279: public boolean isEditable(int index) {
280: if (index == COLUMN_SCALE) {
281: // Allow scale only for numeric datatype
282: return (Types.NUMERIC == column.getJdbcType());
283: }
284: if (index == COLUMN_ISNULLABLE) {
285: return (!column.isPrimaryKey());
286: }
287: return (index != 0);
288: }
289:
290: /**
291: * Indicates whether contents of this RowEntry instance are valid.
292: *
293: * @return true if contents are valid, false otherwise.
294: */
295: public boolean isValid() {
296: return (validateColumnDefinition().isEmpty());
297: }
298:
299: /**
300: * @see org.netbeans.modules.mashup.db.ui.wizard.RowEntryTableModel.RowEntry#setEditable
301: */
302: public void setEditable(int index, boolean newState) {
303: if (index < COLUMN_ID || index > COLUMN_DAFAULT) {
304: throw new IndexOutOfBoundsException();
305: }
306: }
307:
308: /**
309: * @see org.netbeans.modules.mashup.db.ui.wizard.RowEntryTableModel.RowEntry#setValue
310: */
311: public void setValue(int index, Object newValue) {
312: switch (index) {
313: case COLUMN_ID:
314: column.setCardinalPosition(((Integer) newValue)
315: .intValue());
316: break;
317:
318: case COLUMN_PRECLENGTH:
319: column
320: .setPrecision(Integer
321: .parseInt((String) newValue));
322: break;
323:
324: case COLUMN_NAME:
325: column.setName(newValue.toString());
326: break;
327:
328: case COLUMN_JDBCTYPE:
329: column.setJdbcType(SQLUtils.getStdJdbcType(newValue
330: .toString()));
331: break;
332:
333: case COLUMN_SCALE:
334: int scaleValue = isEditable(index) ? Integer
335: .parseInt((String) newValue) : 0;
336: column.setScale(scaleValue);
337: break;
338:
339: case COLUMN_ISNULLABLE:
340: if (column.isPrimaryKey()) {
341: column.setNullable(false);
342: } else {
343: column.setNullable(((Boolean) newValue)
344: .booleanValue());
345: }
346: break;
347:
348: case COLUMN_ISPK:
349: boolean pkFlag = ((Boolean) newValue).booleanValue();
350: column.setPrimaryKey(pkFlag);
351: if (pkFlag) {
352: column.setNullable(false);
353: }
354: break;
355:
356: case COLUMN_DAFAULT:
357: column.setDefaultValue(newValue.toString());
358: break;
359:
360: default:
361: throw new IndexOutOfBoundsException();
362: }
363: }
364: }
365:
366: public static final int COLUMN_DAFAULT = 7;
367: public static final int COLUMN_ID = 0;
368: public static final int COLUMN_ISNULLABLE = 5;
369: public static final int COLUMN_ISPK = 6;
370: public static final int COLUMN_JDBCTYPE = 3;
371: public static final int COLUMN_NAME = 2;
372: public static final int COLUMN_PRECLENGTH = 1;
373: public static final int COLUMN_SCALE = 4;
374:
375: /**
376: * Create new instance of FlatfileColumnTableModel representing the given Collection
377: * of FlatfileDBColumn instances and using header information in the given Map.
378: *
379: * @param columns Collection of FlatfileDBColumns containing model data
380: * @param columnNames List of display header names for the various table columns
381: */
382: public FlatfileColumnTableModel(Collection columns, List columnNames) {
383: super ();
384:
385: editable = new boolean[columnNames.size()];
386: Arrays.fill(editable, true);
387:
388: columnHeaders = (String[]) columnNames
389: .toArray(new String[columnNames.size()]);
390: setRowEntries(columns);
391: }
392:
393: /**
394: * Create new instance of FlatfileColumnTableModel representing the given Flatfile
395: * instance and using header information in the given Map.
396: *
397: * @param aTable FlatfileDBTable containing our data
398: * @param columnNames List of display header names for the various table columns
399: */
400: public FlatfileColumnTableModel(FlatfileDBTable aTable,
401: List columnNames) {
402: this (aTable.getColumnList(), columnNames);
403: }
404:
405: /**
406: * Converts the FlatfileDBColumn instances in the given Collection to ColumnEntry
407: * instances that are compatible with this model.
408: *
409: * @param columns Collection of FlatfileDBColumn instances to be converted
410: * @return List of ColumnEntry instances based on the contents of <code>columns</code>
411: */
412: public List convertToFieldEntries(Collection columns) {
413: List rowEntries = new ArrayList(columns.size());
414: if (columns != null && !columns.isEmpty()) {
415: Iterator iter = columns.iterator();
416: while (iter.hasNext()) {
417: FlatfileDBColumn field = (FlatfileDBColumn) iter.next();
418: rowEntries.add(new ColumnEntry(field));
419: }
420: }
421: return rowEntries;
422: }
423:
424: /**
425: * @see org.netbeans.modules.mashup.db.ui.wizard.RowEntryTableModel#getRowEntries
426: */
427: public synchronized List getRowEntries() {
428: return super .getRowEntries();
429: }
430:
431: /**
432: * @see org.netbeans.modules.mashup.db.ui.wizard.RowEntryTableModel#getRowEntries(int[])
433: */
434: public synchronized List getRowEntries(int[] indices) {
435: return super .getRowEntries(indices);
436: }
437:
438: /**
439: * Gets List of ordinal position IDs of fields, if any, which have invalid names.
440: *
441: * @return List (possibly empty) containing Integer instances, each of which holds the
442: * ordinal position of a column with an invalid name.
443: */
444: public List getRowIdsWithInvalidNames() {
445: List rowlist = Collections.EMPTY_LIST;
446:
447: List entries = getRowEntries();
448: Iterator it = entries.iterator();
449: while (it.hasNext()) {
450: FlatfileColumnTableModel.ColumnEntry fieldEntry = (FlatfileColumnTableModel.ColumnEntry) it
451: .next();
452: String name = fieldEntry.getValue(2).toString();
453: if (!StringUtil.isValid(name, "[A-Za-z]+[A-Za-z_]*")) {
454: if (rowlist == Collections.EMPTY_LIST) {
455: rowlist = new ArrayList(entries.size());
456: }
457:
458: // NOTE: Ordinal position is an Integer object.
459: rowlist.add(fieldEntry.getValue(0));
460: }
461: }
462:
463: return rowlist;
464: }
465:
466: /**
467: * Indicates whether the model contains at least one column with a column length value
468: * of zero.
469: *
470: * @return true if at least one column in this model has a length value of zero; false
471: * otherwise.
472: */
473: public boolean hasZeroLengthColumns() {
474: boolean hasZeroLengthColumn = false;
475:
476: List entries = getRowEntries();
477: Iterator it = entries.iterator();
478: while (it.hasNext()) {
479: FlatfileColumnTableModel.ColumnEntry fieldEntry = (FlatfileColumnTableModel.ColumnEntry) it
480: .next();
481: if (((Integer) fieldEntry.getValue(1)).intValue() == 0) {
482: hasZeroLengthColumn = true;
483: break;
484: }
485: }
486:
487: return hasZeroLengthColumn;
488: }
489:
490: /**
491: * Sets editability of column names in this model.
492: *
493: * @param newValue true if column name is editable, false otherwise
494: */
495: public void setEntryEditable(int index, boolean newValue) {
496: List entries = getRowEntries();
497: Iterator iter = entries.iterator();
498: while (iter.hasNext()) {
499: FlatfileColumnTableModel.ColumnEntry columnEntry = (FlatfileColumnTableModel.ColumnEntry) iter
500: .next();
501: columnEntry.setEditable(index, newValue);
502: }
503: }
504:
505: /**
506: * @see org.netbeans.modules.mashup.db.ui.wizard.RowEntryTableModel#setRowEntries(List)
507: */
508: public synchronized void setRowEntries(Collection columns) {
509: super .setRowEntries(this .convertToFieldEntries(columns));
510: }
511:
512: /**
513: * Updates the given FlatfileDBTable instance to hold FlatfileDBColumn instances which
514: * are defined by this model's current set of row entries.
515: *
516: * @param table FlatfileDBTable whose columns will be populated with the contents of
517: * this model
518: */
519: public void updateColumns(FlatfileDBTable table) {
520: table.deleteAllColumns();
521: Iterator iter = getRowEntries().iterator();
522: while (iter.hasNext()) {
523: ColumnEntry entry = (ColumnEntry) iter.next();
524: table.addColumn(entry.getColumn());
525: }
526: }
527: }
|