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: package org.netbeans.modules.mashup.db.ui.wizard;
0042:
0043: import java.awt.BorderLayout;
0044: import java.awt.Color;
0045: import java.awt.Component;
0046: import java.awt.Dimension;
0047: import java.awt.GridLayout;
0048: import java.awt.event.ItemEvent;
0049: import java.awt.event.ItemListener;
0050: import java.sql.Types;
0051: import java.util.ArrayList;
0052: import java.util.Collection;
0053: import java.util.Collections;
0054: import java.util.Comparator;
0055: import java.util.Iterator;
0056: import java.util.List;
0057: import java.util.ListIterator;
0058: import java.util.MissingResourceException;
0059:
0060: import javax.swing.BorderFactory;
0061: import javax.swing.BoxLayout;
0062: import javax.swing.DefaultCellEditor;
0063: import javax.swing.InputVerifier;
0064: import javax.swing.JComboBox;
0065: import javax.swing.JComponent;
0066: import javax.swing.JLabel;
0067: import javax.swing.JPanel;
0068: import javax.swing.JScrollPane;
0069: import javax.swing.JTable;
0070: import javax.swing.JTextField;
0071: import javax.swing.ListSelectionModel;
0072: import javax.swing.SwingConstants;
0073: import javax.swing.event.ChangeListener;
0074: import javax.swing.event.ListSelectionEvent;
0075: import javax.swing.event.ListSelectionListener;
0076: import javax.swing.event.TableModelEvent;
0077: import javax.swing.event.TableModelListener;
0078: import javax.swing.table.DefaultTableCellRenderer;
0079: import javax.swing.table.TableColumn;
0080: import javax.swing.table.TableColumnModel;
0081: import javax.swing.table.TableModel;
0082:
0083: import org.netbeans.modules.mashup.db.common.PropertyKeys;
0084: import org.netbeans.modules.mashup.db.common.SQLUtils;
0085: import org.netbeans.modules.mashup.db.model.FlatfileDBTable;
0086: import org.netbeans.modules.mashup.db.model.impl.FlatfileDBTableImpl;
0087: import org.netbeans.modules.mashup.tables.wizard.MashupTableWizardIterator;
0088: import org.openide.DialogDisplayer;
0089: import org.openide.NotifyDescriptor;
0090: import org.openide.WizardDescriptor;
0091: import org.openide.util.HelpCtx;
0092: import org.openide.util.NbBundle;
0093:
0094: import java.util.HashSet;
0095: import java.util.Set;
0096: import javax.swing.event.ChangeEvent;
0097: import net.java.hulp.i18n.Logger;
0098: import org.netbeans.modules.etl.logger.Localizer;
0099: import org.netbeans.modules.etl.logger.LogUtil;
0100:
0101: /**
0102: * Captures information needed to determine the record and field layout of a file to be
0103: * imported into an ETL process. <br>
0104: * <br>
0105: * TODO: Add Foreign Key support in flatfile database wizard, show graphical
0106: * representation of the relation between table.
0107: *
0108: * @author Jonathan Giron
0109: * @author Ahimanikya Satapathy
0110: * @version $Revision$
0111: */
0112: public class TableDefinitionPanel implements ListSelectionListener,
0113: WizardDescriptor.FinishablePanel {
0114: private static transient final Logger mLogger = LogUtil
0115: .getLogger(TableDefinitionPanel.class.getName());
0116: private static transient final Localizer mLoc = Localizer.get();
0117:
0118: class BoundedIntegerVerifier extends InputVerifier {
0119: private int max;
0120: private int min;
0121:
0122: public BoundedIntegerVerifier(int minValue, int maxValue) {
0123: if (maxValue < minValue) {
0124: throw new IllegalArgumentException(
0125: "maxValue < minValue!");
0126: }
0127:
0128: min = minValue;
0129: max = maxValue;
0130: }
0131:
0132: public void setMaximum(int newMax) {
0133: if (newMax < min) {
0134: throw new IllegalArgumentException(
0135: "newMax < current minimum value!");
0136: }
0137: max = newMax;
0138: }
0139:
0140: public void setMinimum(int newMin) {
0141: if (newMin > max) {
0142: throw new IllegalArgumentException(
0143: "newMax < current minimum value!");
0144: }
0145: min = newMin;
0146: }
0147:
0148: public boolean verify(JComponent input) {
0149: if (input instanceof JTextField) {
0150: String valStr = ((JTextField) input).getText();
0151: try {
0152: int value = Integer.parseInt(valStr);
0153: return (value >= min && value <= max);
0154: } catch (NumberFormatException ignore) {
0155: return false;
0156: }
0157: }
0158: return false;
0159: }
0160: }
0161:
0162: static class MapperEntry implements RowEntryTableModel.RowEntry {
0163: /**
0164: * Provides a simple comparator to determine whether two unique instances of
0165: * MapperEntry have identical values for columnName.
0166: */
0167: static class UniqueFieldNameComparator implements Comparator {
0168: private static UniqueFieldNameComparator instance;
0169:
0170: public static UniqueFieldNameComparator getInstance() {
0171: if (instance == null) {
0172: instance = new UniqueFieldNameComparator();
0173: }
0174:
0175: return instance;
0176: }
0177:
0178: private UniqueFieldNameComparator() {
0179: }
0180:
0181: /**
0182: * @see java.util.Comparator#compare
0183: */
0184: public int compare(Object o1, Object o2) {
0185: // Specifically return != 0 if (o1 == o2); we want to return 0
0186: // if and only if o1 is a distinct object with a tag ID identical
0187: // to that of o2.
0188: if (o1 == o2) {
0189: return -1;
0190: }
0191:
0192: if (o1 instanceof MapperEntry
0193: && o2 instanceof MapperEntry) {
0194: MapperEntry e1 = (MapperEntry) o1;
0195: MapperEntry e2 = (MapperEntry) o2;
0196:
0197: // Allow for multiple instances of empty strings.
0198: if (e1.columnName != null && e2.columnName != null
0199: && e1.columnName.trim().length() != 0
0200: && e2.columnName.trim().length() != 0) {
0201: return (e1.columnName.compareTo(e2.columnName));
0202: }
0203: // Return some placeholder value other than 0.
0204: return e1.hashCode() - e2.hashCode();
0205: }
0206: throw new ClassCastException(
0207: "Arguments must be MapperEntry instances.");
0208: }
0209: }
0210:
0211: /**
0212: * Provides a simple comparator to determine whether two unique instances of
0213: * MapperEntry have identical values for tagId
0214: */
0215: static class UniqueTagIDComparator implements Comparator {
0216: private static UniqueTagIDComparator instance;
0217:
0218: public static UniqueTagIDComparator getInstance() {
0219: if (instance == null) {
0220: instance = new UniqueTagIDComparator();
0221: }
0222:
0223: return instance;
0224: }
0225:
0226: private UniqueTagIDComparator() {
0227: }
0228:
0229: /**
0230: * @see java.util.Comparator#compare
0231: */
0232: public int compare(Object o1, Object o2) {
0233: // Specifically return != 0 if (o1 == o2); we want to return 0
0234: // if and only if o1 is a distinct object with a tag ID identical
0235: // to that of o2.
0236: if (o1 == o2) {
0237: return -1;
0238: }
0239:
0240: if (o1 instanceof MapperEntry
0241: && o2 instanceof MapperEntry) {
0242: MapperEntry e1 = (MapperEntry) o1;
0243: MapperEntry e2 = (MapperEntry) o2;
0244:
0245: // Allow for multiple instances of empty strings.
0246: if (e1.tagId != null && e2.tagId != null
0247: && e1.tagId.trim().length() != 0
0248: && e2.tagId.trim().length() != 0) {
0249: return (e1.tagId.compareTo(e2.tagId));
0250: }
0251: // Return some placeholder value other than 0.
0252: return e1.hashCode() - e2.hashCode();
0253: }
0254: throw new ClassCastException(
0255: "Arguments must be MapperEntry instances.");
0256: }
0257: }
0258:
0259: private Integer columnLength;
0260: private String columnName;
0261: private String tagId;
0262: private String typeName;
0263:
0264: public MapperEntry() {
0265: this ("", "", 0, "");
0266: }
0267:
0268: public MapperEntry(String id, String name, int length,
0269: String type) {
0270: if (id == null) {
0271: throw new IllegalArgumentException(
0272: "Must supply non-null id");
0273: }
0274:
0275: if (name == null) {
0276: throw new IllegalArgumentException(
0277: "Must supply non-null name");
0278: }
0279:
0280: tagId = id;
0281: columnName = name;
0282: columnLength = (length > 0) ? new Integer(length)
0283: : INTEGER_ZERO;
0284:
0285: // typeName = (isValidFieldType(type)) ? type : FIELD_TYPES[0];
0286: typeName = type;
0287: }
0288:
0289: public String getId() {
0290: return tagId;
0291: }
0292:
0293: public int getLength() {
0294: return columnLength.intValue();
0295: }
0296:
0297: public String getName() {
0298: return columnName;
0299: }
0300:
0301: public String getType() {
0302: return typeName;
0303: }
0304:
0305: public Object getValue(int column) {
0306: switch (column) {
0307: case 0:
0308: return tagId;
0309:
0310: case 1:
0311: return columnLength;
0312:
0313: case 2:
0314: return columnName;
0315:
0316: case 3:
0317: return typeName;
0318:
0319: default:
0320: throw new IndexOutOfBoundsException();
0321: }
0322: }
0323:
0324: public boolean isEditable(int column) {
0325: if (column < 0 || column > 3) {
0326: throw new IndexOutOfBoundsException();
0327: }
0328: return true;
0329: }
0330:
0331: public boolean isValid() {
0332: return (columnName != null && columnName.trim().length() != 0)
0333: && (columnLength.intValue() > 0 && isValidFieldType(typeName));
0334: }
0335:
0336: public boolean isValidFieldType(String type) {
0337: boolean result = false;
0338: return result;
0339: }
0340:
0341: public void setEditable(int column, boolean newState) {
0342: if (column < 0 || column > 3) {
0343: throw new IndexOutOfBoundsException();
0344: }
0345: // Ignore...columns are always editable
0346: }
0347:
0348: public void setValue(int column, Object newValue) {
0349: switch (column) {
0350: case 0:
0351: if (newValue instanceof String) {
0352: tagId = (String) newValue;
0353: }
0354:
0355: case 1:
0356: if (newValue instanceof Integer) {
0357: if (!(columnLength.equals(newValue))) {
0358: columnLength = (Integer) newValue;
0359: }
0360: } else if (newValue instanceof String) {
0361: try {
0362: int val = Integer.parseInt((String) newValue);
0363: if (val != columnLength.intValue() && val > 0) {
0364: columnLength = new Integer(val);
0365: }
0366: } catch (NumberFormatException e) {
0367: // Do nothing
0368: }
0369: }
0370: break;
0371:
0372: case 2:
0373: if (newValue instanceof String) {
0374: columnName = (String) newValue;
0375: }
0376: break;
0377:
0378: case 3:
0379: if (newValue instanceof String) {
0380: typeName = (String) newValue;
0381: }
0382: break;
0383:
0384: default:
0385: throw new IndexOutOfBoundsException();
0386: }
0387: }
0388:
0389: public String toString() {
0390: StringBuilder buf = new StringBuilder();
0391:
0392: buf.append("MapperEntry: tag ID = \"").append(tagId)
0393: .append("\"").append(", name = \"").append(
0394: columnName).append("\"").append(
0395: ", length = ").append(columnLength).append(
0396: ", type = ").append(typeName);
0397:
0398: return buf.toString();
0399: }
0400: }
0401:
0402: class MapperModel extends RowEntryTableModel {
0403: public MapperModel(String[] headers, boolean[] editStates) {
0404: super (headers, editStates);
0405: }
0406:
0407: /**
0408: * Overrides default implementation to check for duplication of tag ID and field
0409: * name values in the model
0410: *
0411: * @param aValue value to be set
0412: * @param rowIndex row of model entry to receive value
0413: * @param columnIndex column of model entry to receive value
0414: * @see RowEntryTableModel#setValueAt
0415: */
0416: public void setValueAt(Object aValue, int rowIndex,
0417: int columnIndex) {
0418: switch (columnIndex) {
0419: case 0:
0420: if (aValue instanceof String) {
0421: if (isDuplicated(getRowEntry(rowIndex),
0422: MapperEntry.UniqueTagIDComparator
0423: .getInstance())) {
0424: DialogDisplayer
0425: .getDefault()
0426: .notify(
0427: new NotifyDescriptor.Message(
0428: NbBundle
0429: .getMessage(
0430: TableDefinitionPanel.MapperModel.class,
0431: "ERROR_import_file_duplicatevalue",
0432: aValue)));
0433: return;
0434: }
0435: }
0436: break;
0437:
0438: case 2:
0439: if (aValue instanceof String) {
0440: if (isDuplicated(getRowEntry(rowIndex),
0441: MapperEntry.UniqueFieldNameComparator
0442: .getInstance())) {
0443: DialogDisplayer
0444: .getDefault()
0445: .notify(
0446: new NotifyDescriptor.Message(
0447: NbBundle
0448: .getMessage(
0449: TableDefinitionPanel.MapperModel.class,
0450: "ERROR_import_file_duplicatevalue",
0451: aValue)));
0452: return;
0453: }
0454: }
0455:
0456: case 1:
0457: case 3:
0458: default:
0459: break;
0460: }
0461:
0462: super .setValueAt(aValue, rowIndex, columnIndex);
0463: }
0464: }
0465:
0466: static {
0467: List headerList = new ArrayList(8);
0468: String nbBundle1 = mLoc.t("PRSR001: #");
0469: try {
0470: headerList.add(Localizer.parse(nbBundle1));
0471: } catch (MissingResourceException e) {
0472: headerList.add("Field #");
0473: }
0474:
0475: String nbBundle2 = mLoc.t("PRSR001: Length");
0476: try {
0477: headerList.add(Localizer.parse(nbBundle2));
0478: } catch (MissingResourceException e) {
0479: headerList.add("Length");
0480: }
0481:
0482: String nbBundle3 = mLoc.t("PRSR001: Name");
0483: try {
0484: headerList.add(Localizer.parse(nbBundle3));
0485: } catch (MissingResourceException e) {
0486: headerList.add("Column Name");
0487: }
0488:
0489: String nbBundle4 = mLoc.t("PRSR001: Datatype");
0490: try {
0491: headerList.add(Localizer.parse(nbBundle4));
0492: } catch (MissingResourceException e) {
0493: headerList.add("Datatype");
0494: }
0495: String nbBundle5 = mLoc.t("PRSR001: Scale");
0496: try {
0497: headerList.add(Localizer.parse(nbBundle5));
0498: } catch (MissingResourceException e) {
0499: headerList.add("Scale");
0500: }
0501: String nbBundle6 = mLoc.t("PRSR001: Null?");
0502: try {
0503: headerList.add(Localizer.parse(nbBundle6));
0504: } catch (MissingResourceException e) {
0505: headerList.add("Null?");
0506: }
0507: String nbBundle7 = mLoc.t("PRSR001: PK?");
0508: try {
0509: headerList.add(Localizer.parse(nbBundle7));
0510: } catch (MissingResourceException e) {
0511: headerList.add("PK?");
0512: }
0513: String nbBundle8 = mLoc.t("PRSR001: Default");
0514: try {
0515: headerList.add(Localizer.parse(nbBundle8));
0516: } catch (MissingResourceException e) {
0517: headerList.add("Default");
0518: }
0519:
0520: COLUMN_HEADERS = Collections.unmodifiableList(headerList);
0521: }
0522:
0523: private static final List COLUMN_HEADERS;
0524:
0525: private static final Integer INTEGER_ZERO = new Integer(0);
0526: private static final int MAX_ERRORS_TO_DISPLAY = 20;
0527: private static final int MAX_LENGTH = 2048;
0528:
0529: private static final int MAX_PRECISION = 38;
0530: private static final int MIN_LENGTH = 0;
0531: private static final int MIN_PRECISION = 1;
0532: private static final int MIN_SCALE = 0;
0533:
0534: private ColumnMetadataTable colMetaTable;
0535: private FlatfileDBTable currentTable;
0536: private JPanel layoutPanel;
0537: private JLabel parseErrorMessage;
0538: private String parserType;
0539: private PreviewDataPanel previewDataPanel;
0540: private BoundedIntegerVerifier scaleVerifier;
0541: private RowEntryTableModel tableModel;
0542: private TableDefinitionVisualPanel panel;
0543: private Component component;
0544: private boolean finish = false;
0545: private int currentIndex = -1;
0546:
0547: /** Creates a new default instance of TableDefinitionPanel */
0548: public TableDefinitionPanel() {
0549: }
0550:
0551: /**
0552: * Overrides parent implementation to allow for addition of this instance as a
0553: * listener for various child components.
0554: */
0555: public void addNotify() {
0556: if (colMetaTable != null) {
0557: ListSelectionModel selModel = colMetaTable
0558: .getSelectionModel();
0559: selModel.removeListSelectionListener(this );
0560: selModel.addListSelectionListener(this );
0561: colMetaTable.requestFocusInWindow();
0562: }
0563: }
0564:
0565: /**
0566: * @see org.netbeans.modules.mashup.db.ui.wizard.AbstractWizardPanel.Content#canAdvance
0567: */
0568: private boolean canAdvance() {
0569: boolean isValid = true;
0570: List errorList = getParseDataErrorMessages();
0571: previewDataPanel.setTable(currentTable);
0572:
0573: if (!getParseDataErrorMessages().isEmpty()) {
0574: Iterator iter = errorList.iterator();
0575: StringBuilder buf = new StringBuilder(100);
0576:
0577: String nbBundle9 = mLoc
0578: .t("PRSR001: Please correct the following error(s) in the current set of field properties.");
0579: buf.append(Localizer.parse(nbBundle9));
0580: while (iter.hasNext()) {
0581: buf.append(iter.next());
0582: }
0583:
0584: DialogDisplayer.getDefault().notify(
0585: new NotifyDescriptor.Message(buf.toString(),
0586: NotifyDescriptor.WARNING_MESSAGE));
0587: isValid = false;
0588: } else if (((FlatfileColumnTableModel) tableModel)
0589: .hasZeroLengthColumns()) {
0590: String nbBundle10 = mLoc
0591: .t("PRSR001: Please supply non-zero length values for each field.");
0592: String errMsg = Localizer.parse(nbBundle10);
0593: DialogDisplayer.getDefault().notify(
0594: new NotifyDescriptor.Message(errMsg,
0595: NotifyDescriptor.WARNING_MESSAGE));
0596: isValid = false;
0597: } else if (!previewDataPanel.showData(null)) {
0598: isValid = false;
0599: }
0600:
0601: return isValid;
0602: }
0603:
0604: /**
0605: * Gets error messages, if any, associated with current set of parse properties.
0606: *
0607: * @return List, possibly empty, of error messages describing problems with current
0608: * parse properties.
0609: */
0610: public List getParseDataErrorMessages() {
0611: List errList = new ArrayList();
0612:
0613: if (tableModel != null) {
0614: int recordLength = 0;
0615: int sumFieldLengths = 0;
0616:
0617: try {
0618: recordLength = Integer
0619: .valueOf(
0620: currentTable
0621: .getProperty(PropertyKeys.WIZARDRECORDLENGTH))
0622: .intValue();
0623: } catch (NumberFormatException ignore) {
0624: // Do nothing...
0625: }
0626:
0627: ListIterator iter = tableModel.getRowEntries()
0628: .listIterator();
0629: List existingColumnNames = new ArrayList(tableModel
0630: .getRowEntries().size());
0631: List duplicateErrorList = new ArrayList();
0632:
0633: while (iter.hasNext()) {
0634: FlatfileColumnTableModel.ColumnEntry entry = (FlatfileColumnTableModel.ColumnEntry) iter
0635: .next();
0636:
0637: sumFieldLengths += entry.getColumn().getPrecision();
0638: errList.addAll(entry.validateColumnDefinition());
0639:
0640: String columnName = entry.getName().toUpperCase();
0641: if (!"".equals(columnName.trim())) { // ignore empty or blank names
0642: int index = existingColumnNames.indexOf(columnName);
0643: if (index != -1) {
0644: String nbBundle11 = mLoc
0645: .t(
0646: "PRSR001: Column #{1}: {0} is already used in column #{2}.",
0647: columnName, new Integer(iter
0648: .nextIndex()),
0649: new Integer(index + 1));
0650: String errMsg = Localizer.parse(nbBundle11);
0651: duplicateErrorList.add(errMsg);
0652: } else {
0653: existingColumnNames.add(columnName);
0654: }
0655: } else {
0656: existingColumnNames.add(""); // Add empty name as placeholder.
0657: }
0658:
0659: // If list size exceeds MAX_ERRORS_TO_DISPLAY, don't append any more
0660: // errors.
0661: if (errList.size() >= MAX_ERRORS_TO_DISPLAY) {
0662: errList.add("\n...\n");
0663: break;
0664: }
0665: }
0666:
0667: String nbBundle12 = mLoc
0668: .t("PRSR001: Column lengths do not sum up to the record length given inStep 2.Please adjust your column values, or go back to Step 2 and correct the record length.");
0669: if (PropertyKeys.FIXEDWIDTH.equalsIgnoreCase(parserType)
0670: && sumFieldLengths != recordLength) {
0671: errList.add(0, Localizer.parse(nbBundle12));
0672: }
0673:
0674: if (errList.size() < MAX_ERRORS_TO_DISPLAY
0675: && !duplicateErrorList.isEmpty()) {
0676: int maximumToShow = Math.min(Math.max(0,
0677: (MAX_ERRORS_TO_DISPLAY - errList.size())),
0678: duplicateErrorList.size());
0679: // Show only up to the first twenty duplications.
0680: if (maximumToShow != 0) {
0681: String nbBundle13 = mLoc
0682: .t("PRSR001: Duplicate column names:");
0683: errList.add(Localizer.parse(nbBundle13));
0684:
0685: errList.addAll(duplicateErrorList.subList(0,
0686: maximumToShow));
0687: if (maximumToShow >= MAX_ERRORS_TO_DISPLAY) {
0688: errList.add("\n...\n");
0689: }
0690: }
0691: }
0692: }
0693:
0694: return errList;
0695: }
0696:
0697: /**
0698: * Indicates whether this panel contains valid content.
0699: *
0700: * @return true if panel is valid and iterator can advance to next panel; false
0701: * otherwise
0702: */
0703: public boolean hasValidData() {
0704: // Determine whether to enable controls in preview pane, depending on whether
0705: // the layout parameters are valid.
0706: boolean isValid = getParseDataErrorMessages().isEmpty();
0707: previewDataPanel.setEnabled(isValid);
0708:
0709: if (parseErrorMessage != null) {
0710: String nbBundle14 = mLoc
0711: .t("PRSR001: Please check for invalid name, length, scale, default values.");
0712: parseErrorMessage.setText(isValid ? " " : Localizer
0713: .parse(nbBundle14));
0714: parseErrorMessage.revalidate();
0715: parseErrorMessage.repaint();
0716: }
0717:
0718: // execute the query and see whether we could get the resultset
0719: if (isValid) {
0720: previewDataPanel.setTable(currentTable);
0721: isValid = previewDataPanel.showData(parseErrorMessage);
0722: }
0723: return isValid;
0724: }
0725:
0726: /**
0727: * @see org.netbeans.modules.mashup.db.ui.wizard.AbstractContentPanel#readSettings
0728: */
0729: public void readSettings(Object settings) {
0730: if (settings instanceof WizardDescriptor) {
0731: WizardDescriptor wd = (WizardDescriptor) settings;
0732: int count = Integer
0733: .parseInt((String) wd
0734: .getProperty(MashupTableWizardIterator.TABLE_INDEX));
0735: currentIndex = count;
0736: if (((List<String>) (wd
0737: .getProperty(MashupTableWizardIterator.URL_LIST)))
0738: .size() - 1 == count) {
0739: finish = true;
0740: } else {
0741: finish = false;
0742: }
0743: boolean shouldClear = true;
0744: FlatfileDBTable tempTable = (FlatfileDBTable) wd
0745: .getProperty(MashupTableWizardIterator.PROP_CURRENTTABLE);
0746: if (currentTable != null
0747: && tempTable.getName().equals(
0748: currentTable.getName())) {
0749: shouldClear = false;
0750: }
0751: currentTable = tempTable;
0752: parserType = currentTable.getParserType();
0753:
0754: if (currentTable != null) {
0755: Collection columns = currentTable.getColumnList();
0756:
0757: if (tableModel == null) {
0758: tableModel = createTableModel(columns);
0759: createLayoutPanel(tableModel);
0760: } else {
0761: if (shouldClear) {
0762: tableModel.clear();
0763: ((FlatfileColumnTableModel) tableModel)
0764: .setRowEntries(columns);
0765: }
0766: previewDataPanel.clearData();
0767: previewDataPanel.setTable(currentTable);
0768: }
0769:
0770: } else {
0771: throw new IllegalStateException(
0772: "Context must contain reference to current flat file.");
0773: }
0774: }
0775: }
0776:
0777: /**
0778: * Overrides parent implementation to allow for removal of this instance as a listener
0779: * for various child components.
0780: */
0781: public void removeNotify() {
0782: if (colMetaTable != null) {
0783: ListSelectionModel selModel = colMetaTable
0784: .getSelectionModel();
0785: selModel.removeListSelectionListener(this );
0786: }
0787: removeNotify();
0788: }
0789:
0790: /**
0791: * @see org.netbeans.modules.mashup.db.ui.wizard.AbstractContentPanel#storeSettings
0792: */
0793: public void storeSettings(Object settings) {
0794: if (settings instanceof WizardDescriptor) {
0795: WizardDescriptor wd = (WizardDescriptor) settings;
0796:
0797: // Don't commit if user didn't click next.
0798: if (wd.getValue() != WizardDescriptor.NEXT_OPTION) {
0799: return;
0800: }
0801:
0802: int index = Integer
0803: .parseInt((String) wd
0804: .getProperty(MashupTableWizardIterator.TABLE_INDEX));
0805: List<String> urls = (List<String>) wd
0806: .getProperty(MashupTableWizardIterator.URL_LIST);
0807:
0808: FlatfileDBTable table = (FlatfileDBTable) wd
0809: .getProperty(MashupTableWizardIterator.PROP_CURRENTTABLE);
0810: if (table == null) {
0811: throw new IllegalStateException(
0812: "Context must contain reference to current flat file.");
0813: }
0814: if (index == currentIndex) {
0815: ((FlatfileColumnTableModel) tableModel)
0816: .updateColumns(currentTable);
0817: table.setProperty(PropertyKeys.WIZARDFIELDCOUNT,
0818: new Integer(table.getColumnList().size()));
0819: ((FlatfileDBTableImpl) table).setOrPutProperty(
0820: PropertyKeys.FILENAME, urls.get(index));
0821: currentIndex = -1;
0822: }
0823: }
0824: }
0825:
0826: /**
0827: * @see javax.swing.event.ListSelectionListener#valueChanged
0828: */
0829: public void valueChanged(ListSelectionEvent e) {
0830: if (e.getValueIsAdjusting()) {
0831: return;
0832: }
0833: }
0834:
0835: private ColumnMetadataTable createColumnMetaTableModel(
0836: TableModel model) {
0837: final ColumnMetadataTable tbl = new ColumnMetadataTable(model);
0838: tbl.setPreferredScrollableViewportSize(new Dimension(400, 100));
0839: model.addTableModelListener(new TableModelListener() {
0840: public void tableChanged(TableModelEvent e) {
0841: fireChangeEvent();
0842: }
0843: });
0844:
0845: TableColumnModel colModel = tbl.getColumnModel();
0846:
0847: // Center field ID label within the colMetaTable cell.
0848: TableColumn aColumn = colModel
0849: .getColumn(FlatfileColumnTableModel.COLUMN_ID);
0850: DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
0851: renderer.setFocusable(false);
0852: renderer.setRequestFocusEnabled(false);
0853: renderer.setHorizontalAlignment(SwingConstants.CENTER);
0854: aColumn.setCellRenderer(renderer);
0855:
0856: // Precision/length renderer/editor.
0857: aColumn = colModel
0858: .getColumn(FlatfileColumnTableModel.COLUMN_PRECLENGTH);
0859:
0860: JTextField colPrecLength = new ColumnSizeTextField(5);
0861:
0862: aColumn.setCellEditor(new DefaultCellEditor(colPrecLength));
0863: aColumn.setCellRenderer(new DefaultTableCellRenderer() {
0864: public Component getTableCellRendererComponent(
0865: JTable aTable, Object value, boolean isSelected,
0866: boolean hasFocus, int row, int column) {
0867: JLabel renderer1 = (JLabel) super
0868: .getTableCellRendererComponent(aTable, value,
0869: isSelected, hasFocus, row, column);
0870:
0871: Object typeObj = aTable.getValueAt(row,
0872: FlatfileColumnTableModel.COLUMN_JDBCTYPE);
0873: Object numericType = SQLUtils
0874: .getStdSqlType(Types.NUMERIC);
0875: Object timeType = SQLUtils.getStdSqlType(Types.TIME);
0876: Object tsType = SQLUtils.getStdSqlType(Types.TIMESTAMP);
0877:
0878: String nbBundle15 = mLoc
0879: .t("PRSR001: Precision of column");
0880: if (numericType != null && numericType.equals(typeObj)) {
0881: renderer1.setToolTipText(Localizer
0882: .parse(nbBundle15));
0883: renderer1
0884: .setHorizontalAlignment(SwingConstants.RIGHT);
0885: } else if (timeType != null
0886: && tsType != null
0887: && (timeType.equals(typeObj) || tsType
0888: .equals(typeObj))) {
0889: String nbBundle16 = mLoc
0890: .t(
0891: "PRSR001: Max length of {0} representation",
0892: typeObj);
0893: renderer1.setToolTipText(Localizer
0894: .parse(nbBundle16));
0895: renderer1
0896: .setHorizontalAlignment(SwingConstants.RIGHT);
0897: } else {
0898: String nbBundle17 = mLoc.t(
0899: "PRSR001: Char length of column", typeObj);
0900: renderer1.setToolTipText(Localizer
0901: .parse(nbBundle17));
0902: renderer1
0903: .setHorizontalAlignment(SwingConstants.RIGHT);
0904: }
0905:
0906: return renderer1;
0907: }
0908: });
0909:
0910: // Column name renderer/editor.
0911: final JTextField columnName = new ColumnNameTextField();
0912:
0913: aColumn = colModel
0914: .getColumn(FlatfileColumnTableModel.COLUMN_NAME);
0915: aColumn.setCellEditor(new DefaultCellEditor(columnName));
0916: aColumn.setCellRenderer(new DefaultTableCellRenderer() {
0917: public Component getTableCellRendererComponent(
0918: JTable aTable, Object value, boolean isSelected,
0919: boolean hasFocus, int row, int column) {
0920: JLabel renderer1 = (JLabel) super
0921: .getTableCellRendererComponent(aTable, value,
0922: isSelected, hasFocus, row, column);
0923:
0924: TableModel model1 = aTable.getModel();
0925: Object obj = model1.getValueAt(row, column);
0926: renderer1.setToolTipText(obj != null ? obj.toString()
0927: : null);
0928: renderer1
0929: .setHorizontalAlignment(SwingConstants.LEADING);
0930:
0931: return renderer1;
0932: }
0933: });
0934:
0935: // Make scale field editable only if type selected is Types.NUMERIC.
0936: final JTextField colScale = new ColumnSizeTextField(5);
0937:
0938: JComboBox sqlTypeBox = tbl.setComboBoxRenderer(
0939: FlatfileColumnTableModel.COLUMN_JDBCTYPE, SQLUtils
0940: .getStdSqlTypes());
0941: sqlTypeBox.addItemListener(new ItemListener() {
0942: public void itemStateChanged(ItemEvent e) {
0943: Object o = e.getItem();
0944: if (ItemEvent.SELECTED == e.getStateChange()
0945: && o instanceof String) {
0946: // Force a repaint to redraw scale display for affected row.
0947: tbl.revalidate();
0948: tbl.repaint();
0949: }
0950: }
0951: });
0952:
0953: aColumn = colModel
0954: .getColumn(FlatfileColumnTableModel.COLUMN_SCALE);
0955: aColumn.setCellEditor(new DefaultCellEditor(colScale) {
0956: public Component getTableCellEditorComponent(JTable aTable,
0957: Object value, boolean isSelected, int row,
0958: int column) {
0959: Object typeObj = aTable.getValueAt(row,
0960: FlatfileColumnTableModel.COLUMN_JDBCTYPE);
0961: Object numericType = SQLUtils
0962: .getStdSqlType(Types.NUMERIC);
0963: JComponent editor = new JLabel();
0964:
0965: boolean isNumericType = (numericType != null && numericType
0966: .equals(typeObj));
0967: if (isNumericType) {
0968: editor = (JComponent) super
0969: .getTableCellEditorComponent(aTable, value,
0970: isSelected, row, column);
0971: editor.setEnabled(true);
0972: } else {
0973: JLabel lbl = (JLabel) editor;
0974: String nbBundle18 = mLoc.t("PRSR001: N/A");
0975: lbl.setText(Localizer.parse(nbBundle18));
0976: String nbBundle19 = mLoc.t(
0977: "PRSR001: Not defined for datatype {0}",
0978: typeObj);
0979: lbl.setToolTipText(Localizer.parse(nbBundle19));
0980: lbl.setHorizontalAlignment(SwingConstants.CENTER);
0981: }
0982:
0983: return editor;
0984: }
0985: });
0986: aColumn.setCellRenderer(new DefaultTableCellRenderer() {
0987: public Component getTableCellRendererComponent(
0988: JTable aTable, Object value, boolean isSelected,
0989: boolean hasFocus, int row, int column) {
0990: JLabel renderer1 = (JLabel) super
0991: .getTableCellRendererComponent(aTable, value,
0992: isSelected, hasFocus, row, column);
0993:
0994: Object typeObj = aTable.getValueAt(row,
0995: FlatfileColumnTableModel.COLUMN_JDBCTYPE);
0996: Object numericType = SQLUtils
0997: .getStdSqlType(Types.NUMERIC);
0998: if (numericType != null && numericType.equals(typeObj)) {
0999: String nbBundle20 = mLoc
1000: .t("PRSR001: Numeric scale (must be less than or equal to precision)");
1001: renderer1.setToolTipText(Localizer
1002: .parse(nbBundle20));
1003: renderer1
1004: .setHorizontalAlignment(SwingConstants.RIGHT);
1005: } else {
1006: String nbBundle21 = mLoc.t("PRSR001: N/A");
1007: renderer1.setText(Localizer.parse(nbBundle21));
1008: String nbBundle22 = mLoc.t(
1009: "PRSR001: Not defined for datatype {0}",
1010: typeObj);
1011: renderer1.setToolTipText(Localizer
1012: .parse(nbBundle22));
1013: renderer1
1014: .setHorizontalAlignment(SwingConstants.CENTER);
1015: }
1016:
1017: return renderer1;
1018: }
1019: });
1020:
1021: // Add checkbox to set nullability of field
1022: aColumn = colModel
1023: .getColumn(FlatfileColumnTableModel.COLUMN_ISNULLABLE);
1024: final ColumnMetadataTable.MyBooleanRenderer nullrenderer = new ColumnMetadataTable.MyBooleanRenderer();
1025: aColumn.setCellEditor(new DefaultCellEditor(nullrenderer) {
1026: public Component getTableCellEditorComponent(JTable aTable,
1027: Object value, boolean isSelected, int row,
1028: int column) {
1029: Object typeObj = aTable.getValueAt(row,
1030: FlatfileColumnTableModel.COLUMN_ISPK);
1031: JComponent editor = new JLabel();
1032:
1033: if (Boolean.TRUE.equals(typeObj)) {
1034: nullrenderer.setSelected(false);
1035: JLabel lbl = (JLabel) editor;
1036: String nbBundle23 = mLoc.t("PRSR001: N/A");
1037: lbl.setText(Localizer.parse(nbBundle23));
1038: String nbBundle24 = mLoc.t(
1039: "PRSR001: Not defined for datatype {0}",
1040: typeObj);
1041: lbl.setToolTipText(Localizer.parse(nbBundle24));
1042: lbl.setDisplayedMnemonic(Localizer
1043: .parse(nbBundle24).charAt(0));
1044: lbl.setHorizontalAlignment(SwingConstants.CENTER);
1045: } else {
1046: editor = (JComponent) nullrenderer
1047: .getTableCellRendererComponent(aTable,
1048: value, isSelected, isSelected, row,
1049: column);
1050: }
1051: return editor;
1052: }
1053: });
1054:
1055: // Add checkbox to set nullability of field
1056: aColumn = colModel
1057: .getColumn(FlatfileColumnTableModel.COLUMN_ISPK);
1058: final ColumnMetadataTable.MyBooleanRenderer pkrenderer = new ColumnMetadataTable.MyBooleanRenderer();
1059: aColumn.setCellEditor(new DefaultCellEditor(pkrenderer) {
1060: public Component getTableCellEditorComponent(JTable aTable,
1061: Object value, boolean isSelected, int row,
1062: int column) {
1063: return pkrenderer.getTableCellRendererComponent(aTable,
1064: value, isSelected, isSelected, row, column);
1065: }
1066: });
1067: pkrenderer.addItemListener(new ItemListener() {
1068: public void itemStateChanged(ItemEvent e) {
1069: if (ItemEvent.SELECTED == e.getStateChange()) {
1070: // Force a repaint to redraw scale display for affected row.
1071: tbl.revalidate();
1072: tbl.repaint();
1073: }
1074: }
1075: });
1076:
1077: // Defalt value renderer/editor.
1078: final JTextField defaultValue = new JTextField();
1079:
1080: aColumn = colModel
1081: .getColumn(FlatfileColumnTableModel.COLUMN_DAFAULT);
1082: aColumn.setCellEditor(new DefaultCellEditor(defaultValue));
1083: aColumn.setCellRenderer(new DefaultTableCellRenderer() {
1084: public Component getTableCellRendererComponent(
1085: JTable aTable, Object value, boolean isSelected,
1086: boolean hasFocus, int row, int column) {
1087: JLabel renderer1 = (JLabel) super
1088: .getTableCellRendererComponent(aTable, value,
1089: isSelected, hasFocus, row, column);
1090:
1091: TableModel model1 = aTable.getModel();
1092: Object obj = model1.getValueAt(row, column);
1093: renderer1.setToolTipText(obj != null ? obj.toString()
1094: : null);
1095: renderer1
1096: .setHorizontalAlignment(SwingConstants.LEADING);
1097:
1098: return renderer1;
1099: }
1100: });
1101:
1102: return tbl;
1103: }
1104:
1105: /*
1106: * Creates layout panel using the given TableModel and display configuration info in
1107: * the given VisualParamGroup.
1108: */
1109: private void createLayoutPanel(TableModel model) {
1110: layoutPanel.removeAll();
1111: layoutPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
1112: layoutPanel.setAlignmentY(Component.TOP_ALIGNMENT);
1113:
1114: layoutPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0,
1115: 0));
1116: layoutPanel.setLayout(new GridLayout(2, 1));
1117:
1118: // Set up listeners for interaction between colMetaTable selections and buttons.
1119: JPanel tablePanel = new JPanel();
1120: String nbBundle25 = mLoc.t("PRSR001: Column Definition");
1121: String tableLabel = Localizer.parse(nbBundle25);
1122: tablePanel.setBorder(BorderFactory
1123: .createTitledBorder(tableLabel));
1124:
1125: tablePanel.setLayout(new BorderLayout());
1126:
1127: colMetaTable = createColumnMetaTableModel(model);
1128: JScrollPane tableViewer = new JScrollPane(colMetaTable);
1129:
1130: parseErrorMessage = new JLabel(" ");
1131: parseErrorMessage
1132: .setHorizontalAlignment(SwingConstants.LEADING);
1133: parseErrorMessage.setForeground(Color.RED);
1134: parseErrorMessage.setBorder(BorderFactory.createEmptyBorder(0,
1135: 2, 0, 2));
1136: tablePanel.add(parseErrorMessage, BorderLayout.NORTH);
1137: tablePanel.add(tableViewer, BorderLayout.CENTER);
1138:
1139: // Add field properties colMetaTable to panel.
1140: layoutPanel.add(tablePanel);
1141:
1142: previewDataPanel = new PreviewDataPanel(currentTable);
1143: previewDataPanel
1144: .setTableModel((FlatfileColumnTableModel) model);
1145: // Add preview output to panel.
1146: layoutPanel.add(previewDataPanel, BorderLayout.SOUTH);
1147: layoutPanel.invalidate();
1148: layoutPanel.revalidate();
1149:
1150: fireChangeEvent();
1151: }
1152:
1153: private RowEntryTableModel createTableModel(Collection fields) {
1154: RowEntryTableModel newModel = new FlatfileColumnTableModel(
1155: fields, COLUMN_HEADERS);
1156:
1157: newModel.addTableModelListener(new TableModelListener() {
1158: public void tableChanged(TableModelEvent e) {
1159: TableModel model = (TableModel) e.getSource();
1160: int updateType = e.getType();
1161: int updatedColumn = e.getColumn();
1162:
1163: if (TableModelEvent.UPDATE == updateType) {
1164: int firstRow = Math.max(0, e.getFirstRow());
1165: int lastRow = Math.min(e.getLastRow(), model
1166: .getRowCount() - 1);
1167: for (int rowIndex = firstRow; rowIndex <= lastRow; rowIndex++) {
1168: boolean isNumericType = isJdbcTypeNumeric(
1169: model, rowIndex);
1170:
1171: switch (updatedColumn) {
1172: case FlatfileColumnTableModel.COLUMN_JDBCTYPE:
1173: int maxPrecLength = 0;
1174: int minPrecLength = 0;
1175:
1176: if (isNumericType) {
1177: maxPrecLength = MAX_PRECISION;
1178: minPrecLength = MIN_PRECISION;
1179: } else {
1180: maxPrecLength = MAX_LENGTH;
1181: minPrecLength = MIN_LENGTH;
1182: }
1183:
1184: {
1185: int maxScale = enforceBounds(
1186: model,
1187: rowIndex,
1188: FlatfileColumnTableModel.COLUMN_PRECLENGTH,
1189: maxPrecLength, minPrecLength);
1190: enforceBounds(
1191: model,
1192: rowIndex,
1193: FlatfileColumnTableModel.COLUMN_SCALE,
1194: maxScale, MIN_SCALE);
1195: if (scaleVerifier != null) {
1196: scaleVerifier.setMaximum(maxScale);
1197: }
1198: }
1199: break;
1200:
1201: case FlatfileColumnTableModel.COLUMN_PRECLENGTH:
1202: if (isJdbcTypeNumeric(model, rowIndex)) {
1203: int maxScale = getInt(
1204: model,
1205: rowIndex,
1206: FlatfileColumnTableModel.COLUMN_PRECLENGTH);
1207: enforceBounds(
1208: model,
1209: rowIndex,
1210: FlatfileColumnTableModel.COLUMN_SCALE,
1211: maxScale, MIN_SCALE);
1212: if (scaleVerifier != null) {
1213: scaleVerifier.setMaximum(maxScale);
1214: }
1215: }
1216: break;
1217:
1218: default:
1219: break;
1220: }
1221: }
1222: }
1223: }
1224:
1225: private int enforceBounds(TableModel model, int row,
1226: int col, int max, int min) {
1227: int modelValue = getInt(model, row, col);
1228:
1229: if (modelValue < min) {
1230: model.setValueAt(new Integer(min), row, col);
1231: modelValue = min;
1232: } else if (modelValue > max) {
1233: model.setValueAt(new Integer(max), row, col);
1234: modelValue = max;
1235: }
1236:
1237: return modelValue;
1238: }
1239:
1240: private int getInt(TableModel model, int row, int col) {
1241: int modelValue = 0;
1242:
1243: Object val = model.getValueAt(row, col);
1244: if (val instanceof Number) {
1245: modelValue = ((Number) val).intValue();
1246: } else {
1247: try {
1248: modelValue = Integer.parseInt((String) val);
1249: } catch (NumberFormatException ignore) {
1250: modelValue = 0;
1251: }
1252: }
1253:
1254: return modelValue;
1255: }
1256:
1257: private boolean isJdbcTypeNumeric(TableModel model, int row) {
1258: Object numericType = SQLUtils
1259: .getStdSqlType(Types.NUMERIC);
1260: Object typeObj = model.getValueAt(row,
1261: FlatfileColumnTableModel.COLUMN_JDBCTYPE);
1262: return (typeObj != null && typeObj.equals(numericType));
1263: }
1264: });
1265:
1266: return newModel;
1267: }
1268:
1269: private void init() {
1270: panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
1271: panel.setPreferredSize(new Dimension(205, 130));
1272:
1273: String nbBundle26 = mLoc
1274: .t("PRSR001: Define table and column definition for this file.");
1275: JLabel instr = new JLabel(Localizer.parse(nbBundle26));
1276: instr.setDisplayedMnemonic(Localizer.parse(nbBundle26)
1277: .charAt(0));
1278: instr.setAlignmentX(Component.LEFT_ALIGNMENT);
1279: layoutPanel = new JPanel(); // Column layout subpanel
1280:
1281: panel.add(instr);
1282: panel.add(layoutPanel);
1283: }
1284:
1285: public Component getComponent() {
1286: if (component == null) {
1287: panel = new TableDefinitionVisualPanel(this );
1288: init();
1289: component = (Component) panel;
1290: }
1291: return component;
1292: }
1293:
1294: public HelpCtx getHelp() {
1295: return HelpCtx.DEFAULT_HELP;
1296: }
1297:
1298: private final Set<ChangeListener> listeners = new HashSet<ChangeListener>(
1299: 1);
1300:
1301: public final void addChangeListener(ChangeListener l) {
1302: synchronized (listeners) {
1303: listeners.add(l);
1304: }
1305: }
1306:
1307: public final void removeChangeListener(ChangeListener l) {
1308: synchronized (listeners) {
1309: listeners.remove(l);
1310: }
1311: }
1312:
1313: protected final void fireChangeEvent() {
1314: Iterator<ChangeListener> it;
1315: synchronized (listeners) {
1316: it = new HashSet<ChangeListener>(listeners).iterator();
1317: }
1318: ChangeEvent ev = new ChangeEvent(this );
1319: while (it.hasNext()) {
1320: it.next().stateChanged(ev);
1321: }
1322: }
1323:
1324: public boolean isFinishPanel() {
1325: return finish && canAdvance();
1326: }
1327:
1328: public boolean isValid() {
1329: return canAdvance();
1330: }
1331: }
|