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.awt.Component;
044: import java.awt.Dimension;
045: import java.awt.Font;
046: import java.awt.GridBagConstraints;
047: import java.awt.GridBagLayout;
048: import java.beans.PropertyChangeEvent;
049: import java.beans.PropertyChangeListener;
050: import java.beans.PropertyVetoException;
051: import java.beans.VetoableChangeListener;
052: import java.io.BufferedReader;
053: import java.io.File;
054: import java.io.FileInputStream;
055: import java.io.IOException;
056: import java.io.InputStream;
057: import java.io.InputStreamReader;
058: import java.net.URL;
059: import java.util.Iterator;
060: import java.util.List;
061: import java.util.Map;
062:
063: import javax.swing.BorderFactory;
064: import javax.swing.JComponent;
065: import javax.swing.JLabel;
066: import javax.swing.JPanel;
067: import javax.swing.JScrollPane;
068: import javax.swing.JTextArea;
069: import javax.swing.event.ChangeListener;
070: import jxl.Sheet;
071: import jxl.Workbook;
072:
073: import org.netbeans.modules.mashup.db.bootstrap.FlatfileBootstrapParser;
074: import org.netbeans.modules.mashup.db.bootstrap.FlatfileBootstrapParserFactory;
075: import org.netbeans.modules.mashup.db.common.FlatfileDBException;
076: import org.netbeans.modules.mashup.db.common.Property;
077: import org.netbeans.modules.mashup.db.common.PropertyKeys;
078: import org.netbeans.modules.mashup.db.common.SQLUtils;
079: import org.netbeans.modules.mashup.db.model.FlatfileDBColumn;
080: import org.netbeans.modules.mashup.db.model.FlatfileDBTable;
081: import org.netbeans.modules.mashup.db.model.impl.FlatfileDBTableImpl;
082: import org.netbeans.modules.mashup.db.ui.resource.FlatfileDBResourceManager;
083: import org.netbeans.modules.mashup.tables.wizard.MashupTableWizardIterator;
084: import org.netbeans.modules.sql.framework.ui.editor.property.IPropertySheet;
085: import org.netbeans.modules.sql.framework.ui.editor.property.impl.PropertyViewManager;
086: import org.openide.DialogDisplayer;
087: import org.openide.NotifyDescriptor;
088: import org.openide.WizardDescriptor;
089: import net.java.hulp.i18n.Logger;
090: import com.sun.sql.framework.utils.StringUtil;
091: import java.util.HashSet;
092: import java.util.Set;
093: import javax.swing.event.ChangeEvent;
094: import jxl.Cell;
095: import org.netbeans.modules.etl.logger.Localizer;
096: import org.netbeans.modules.etl.logger.LogUtil;
097: import org.openide.util.HelpCtx;
098:
099: /**
100: * Captures information needed to determine the parsing configuration of a file to be
101: * imported into an ETL process.
102: *
103: * @author Jonathan Giron
104: * @author Ahimanikya Satapathy
105: * @version $Revision$
106: */
107: public class ParseContentPanel implements PropertyChangeListener,
108: VetoableChangeListener, WizardDescriptor.Panel {
109:
110: private static transient final Logger mLogger = LogUtil
111: .getLogger(ParseContentPanel.class.getName());
112: private static transient final Localizer mLoc = Localizer.get();
113: private static final String LOG_CATEGORY = ParseContentPanel.class
114: .getName();
115: /* Map of current parse properties (prior to displaying panel) */
116: private Map currentPropertyMap;
117: /* Local reference to current file */
118: private FlatfileDBTable currentTable;
119: /*
120: * String to hold error messages, if any, related to invalid panel contents.
121: */
122: private transient String parseErrors;
123: /* Descriptor for parse properties; generates JComponent */
124: private IPropertySheet propertySheet;
125: private int currentIndex = -1;
126: private Component component;
127: private ParseContentVisualPanel panel;
128:
129: /** Creates a new default instance of ParseContentPanel */
130: public ParseContentPanel() {
131: }
132:
133: private boolean canAdvance() {
134: if (parseErrors != null) {
135: DialogDisplayer.getDefault().notify(
136: new NotifyDescriptor.Message(parseErrors.trim(),
137: NotifyDescriptor.WARNING_MESSAGE));
138: parseErrors = null; // Blank out the (consumed) error message.
139: return false;
140: }
141: return true;
142: }
143:
144: /**
145: * Indicates whether this panel contains valid content.
146: *
147: * @return true if panel is valid and iterator can advance to next panel; false
148: * otherwise
149: * @see org.netbeans.modules.etl.ui.netbeans.wizards.AbstractWizardPanel$Content#hasValidData
150: */
151: public boolean hasValidData() {
152: return (propertySheet != null) ? propertySheet
153: .getPropertyGroup("Default").isValid() : false;
154: }
155:
156: public boolean isRefreshRequired(FlatfileDBTable table,
157: Map newProperties) {
158: final Map tableProps = table.getProperties();
159: Iterator iter = tableProps.keySet().iterator();
160: while (iter.hasNext()) {
161: String key = (String) iter.next();
162: Property property = (Property) tableProps.get(key);
163:
164: Object newObj = newProperties.get(key);
165: Object oldObj = currentPropertyMap.get(key);
166:
167: if (newObj != null && oldObj == null) {
168: return true;
169: }
170: if ((!oldObj.equals(newObj))
171: && property.isRefreshRequired()) {
172: return true;
173: }
174: }
175: return false;
176: }
177:
178: /**
179: * This method gets called when a bound property is changed.
180: *
181: * @param evt A PropertyChangeEvent object describing the event source and the
182: * property that has changed.
183: */
184: public void propertyChange(PropertyChangeEvent evt) {
185: fireChangeEvent();
186: }
187:
188: /**
189: * Read temporary configuration and content information from the given context object.
190: *
191: * @param settings Context object containing information to configure this panel's
192: * display.
193: */
194: public void readSettings(Object settings) {
195: if (settings instanceof WizardDescriptor) {
196: WizardDescriptor wd = (WizardDescriptor) settings;
197:
198: currentTable = (FlatfileDBTable) wd
199: .getProperty(MashupTableWizardIterator.PROP_CURRENTTABLE);
200: if (currentTable == null) {
201: throw new IllegalStateException(
202: "Context must contain reference to current flat file.");
203: }
204:
205: panel.removeAll();
206: String nbBundle1 = mLoc
207: .t("PRSR001: Supply the following information required to parse this file.");
208: JLabel instr = new JLabel(Localizer.parse(nbBundle1));
209: instr.getAccessibleContext().setAccessibleName(
210: Localizer.parse(nbBundle1));
211: instr.setAlignmentX(Component.LEFT_ALIGNMENT);
212: instr.setDisplayedMnemonic(Localizer.parse(nbBundle1)
213: .charAt(0));
214: GridBagConstraints gbc = new GridBagConstraints();
215: gbc.anchor = GridBagConstraints.FIRST_LINE_START;
216: gbc.fill = GridBagConstraints.HORIZONTAL;
217: gbc.gridx = 0;
218: gbc.gridy = 0;
219: gbc.weighty = 0;
220: panel.add(instr, gbc);
221: panel.setPreferredSize(new Dimension(150, 150));
222: // do the guess work for the 1st time
223: if (0 == currentTable.getColumnList().size()) {
224: try {
225: FlatfileBootstrapParser parser = FlatfileBootstrapParserFactory
226: .getInstance().getBootstrapParser(
227: currentTable.getParserType());
228: if (parser != null) {
229: parser.makeGuess(currentTable);
230: }
231: } catch (FlatfileDBException se) {
232: // ignore
233: }
234: }
235:
236: PropertyViewManager pvMgr = getPropertyViewManager();
237:
238: String fieldSep = currentTable
239: .getProperty(PropertyKeys.WIZARDCUSTOMFIELDDELIMITER);
240: if (!StringUtil.isNullString(fieldSep)) {
241: currentTable.setProperty(PropertyKeys.FIELDDELIMITER,
242: "UserDefined");
243: }
244:
245: currentPropertyMap = Property
246: .createKeyValueMapFrom(currentTable.getProperties());
247:
248: propertySheet = pvMgr.getPropertySheet(currentPropertyMap,
249: currentTable.getParserType());
250:
251: propertySheet.getPropertyGroup("Default")
252: .addPropertyChangeListener(this );
253: propertySheet.getPropertyGroup("Default")
254: .addVetoableChangeListener(this );
255:
256: gbc = new GridBagConstraints();
257: gbc.anchor = GridBagConstraints.LINE_START;
258: gbc.fill = GridBagConstraints.BOTH;
259: gbc.gridx = 0;
260: gbc.gridy = GridBagConstraints.RELATIVE;
261: gbc.weightx = 50.0;
262: gbc.weighty = 55.0;
263: panel.add(propertySheet.getPropertySheet(), gbc);
264:
265: gbc = new GridBagConstraints();
266: gbc.anchor = GridBagConstraints.LINE_START;
267: gbc.fill = GridBagConstraints.BOTH;
268: gbc.gridx = 0;
269: gbc.gridy = GridBagConstraints.RELATIVE;
270: gbc.weightx = 50.0;
271: gbc.weighty = 45.0;
272: panel.add(getPreviewPanel(wd), gbc);
273:
274: parseErrors = null;
275:
276: currentIndex = Integer
277: .parseInt((String) wd
278: .getProperty(MashupTableWizardIterator.TABLE_INDEX));
279: }
280: }
281:
282: /**
283: * Gets instance of PropertyViewManager which supplies parse property view components.
284: *
285: * @return PropertyViewManager instance associated with this iterator.
286: * @see PropertyViewManager
287: */
288: private static PropertyViewManager getPropertyViewManager() {
289: InputStream stream = ParseContentPanel.class
290: .getClassLoader()
291: .getResourceAsStream(
292: "org/netbeans/modules/mashup/db/ui/resource/parse_properties.xml");
293: return new PropertyViewManager(stream,
294: new FlatfileDBResourceManager());
295: }
296:
297: /**
298: * Write temporary configuration and content information to the given context object.
299: *
300: * @param settings Context object to receive config and content information.
301: */
302: public void storeSettings(Object settings) {
303: if (settings instanceof WizardDescriptor) {
304: WizardDescriptor wd = (WizardDescriptor) settings;
305:
306: // Don't commit if user didn't click next.
307: if (wd.getValue() != WizardDescriptor.NEXT_OPTION) {
308: return;
309: }
310:
311: if (currentTable == null) {
312: currentTable = (FlatfileDBTable) wd
313: .getProperty(MashupTableWizardIterator.PROP_CURRENTTABLE);
314: if (currentTable == null) {
315: throw new IllegalStateException(
316: "Context must contain reference to current flat file.");
317: }
318: }
319:
320: // get url.
321: int index = Integer
322: .parseInt((String) wd
323: .getProperty(MashupTableWizardIterator.TABLE_INDEX));
324: List<String> urls = (List<String>) wd
325: .getProperty(MashupTableWizardIterator.URL_LIST);
326:
327: if (index == currentIndex) {
328: currentTable.updateProperties(propertySheet
329: .getPropertyValues());
330: currentTable.setProperty("FILENAME", urls.get(index));
331: Map newPropertyMap = Property
332: .createKeyValueMapFrom(currentTable
333: .getProperties());
334:
335: // If any properties change, rebuild set of fields.
336: if (0 == currentTable.getColumnList().size()
337: || !newPropertyMap.equals(currentPropertyMap)) {
338: try {
339: final String loadType = (String) currentPropertyMap
340: .get(PropertyKeys.LOADTYPE);
341:
342: if (isRefreshRequired(currentTable,
343: newPropertyMap)) {
344: currentTable.deleteAllColumns();
345: }
346:
347: FlatfileBootstrapParser parser = FlatfileBootstrapParserFactory
348: .getInstance().getBootstrapParser(
349: currentTable.getParserType());
350: ((FlatfileDBTableImpl) currentTable)
351: .setOrPutProperty(PropertyKeys.URL,
352: urls.get(index));
353: ((FlatfileDBTableImpl) currentTable)
354: .setOrPutProperty(
355: PropertyKeys.FILENAME, urls
356: .get(index));
357: List colList = parser
358: .buildFlatfileDBColumns(currentTable);
359: if (colList != null && !colList.isEmpty()) {
360: currentTable.deleteAllColumns();
361: Iterator iter = colList.iterator();
362: try {
363: while (iter.hasNext()) {
364: currentTable
365: .addColumn((FlatfileDBColumn) iter
366: .next());
367: }
368: } catch (IllegalArgumentException e) {
369: // ignore
370: }
371: }
372:
373: try {
374:
375: final String sqlType = (String) newPropertyMap
376: .get(PropertyKeys.WIZARDDEFAULTSQLTYPE);
377: final String oldsqlType = (String) currentPropertyMap
378: .get(PropertyKeys.WIZARDDEFAULTSQLTYPE);
379:
380: Iterator iter = currentTable
381: .getColumnList().iterator();
382: while (iter.hasNext() && sqlType != null) {
383: FlatfileDBColumn newFld = (FlatfileDBColumn) iter
384: .next();
385:
386: if (!sqlType.equals(oldsqlType)) {
387: newFld.setJdbcType(SQLUtils
388: .getStdJdbcType(sqlType));
389: }
390:
391: if (loadType
392: .equals(PropertyKeys.DELIMITED)) {
393: final Integer precision = (Integer) newPropertyMap
394: .get(PropertyKeys.WIZARDDEFAULTPRECISION);
395: final Integer oldPrecision = (Integer) currentPropertyMap
396: .get(PropertyKeys.WIZARDDEFAULTPRECISION);
397:
398: if (!precision.equals(oldPrecision)) {
399: newFld.setPrecision(precision
400: .intValue());
401: }
402: }
403: }
404: } catch (Exception ignore) {
405: // do nothing; current fields will use their default values.
406: }
407: parseErrors = null;
408: currentIndex = -1;
409: wd
410: .putProperty(
411: MashupTableWizardIterator.PROP_CURRENTTABLE,
412: currentTable);
413: } catch (Exception e) {
414: parseErrors = e.getMessage();
415: }
416: } else {
417: parseErrors = null;
418: }
419: }
420: }
421: }
422:
423: /**
424: * This method gets called when a constrained property is changed.
425: *
426: * @param evt a <code>PropertyChangeEvent</code> object describing the event source
427: * and the property that has changed.
428: * @exception PropertyVetoException if the recipient wishes the property change to be
429: * rolled back.
430: */
431: public void vetoableChange(PropertyChangeEvent evt)
432: throws PropertyVetoException {
433: }
434:
435: private JComponent getPreviewPanel(WizardDescriptor wd) {
436: JPanel panel = new JPanel();
437: panel.setLayout(new GridBagLayout());
438: String nbBundle2 = mLoc.t("PRSR001: Preview of file");
439: panel.setBorder(BorderFactory.createCompoundBorder(
440: BorderFactory.createTitledBorder(Localizer
441: .parse(nbBundle2)), BorderFactory
442: .createEmptyBorder(4, 4, 4, 4)));
443: panel.setPreferredSize(new Dimension(150, 100));
444: JLabel lbl = new JLabel("");
445: GridBagConstraints gbc = new GridBagConstraints();
446: gbc.anchor = GridBagConstraints.FIRST_LINE_START;
447: gbc.fill = GridBagConstraints.HORIZONTAL;
448: gbc.gridx = 0;
449: gbc.gridy = 0;
450: gbc.weightx = 50.0;
451: gbc.weighty = 0.0;
452: panel.add(lbl, gbc);
453:
454: JTextArea txtArea = new JTextArea();
455: txtArea.setEditable(false);
456: txtArea.setFont(new Font("Courier", Font.PLAIN, 12));
457: txtArea.setText(readPreviewText(wd));
458:
459: gbc = new GridBagConstraints();
460: gbc.anchor = GridBagConstraints.LINE_START;
461: gbc.fill = GridBagConstraints.BOTH;
462: gbc.gridx = 0;
463: gbc.gridy = GridBagConstraints.RELATIVE;
464: gbc.weightx = 50.0;
465: gbc.weighty = 100.0;
466: JScrollPane sp = new JScrollPane(txtArea);
467: panel.add(sp, gbc);
468:
469: // Ensure scrollpane text area starts at top of document.
470: txtArea.setCaretPosition(0);
471: return panel;
472: }
473:
474: private String readPreviewText(WizardDescriptor wd) {
475: String record = "";
476: BufferedReader br = null;
477: FlatfileDBTable table = (FlatfileDBTable) wd
478: .getProperty(MashupTableWizardIterator.PROP_CURRENTTABLE);
479: List<String> urls = (List<String>) wd
480: .getProperty(MashupTableWizardIterator.URL_LIST);
481: int index = Integer.parseInt((String) wd
482: .getProperty(MashupTableWizardIterator.TABLE_INDEX));
483: String encoding = table.getEncodingScheme();
484: final int maxCharsToRead = 1024;
485: final int maxCharsToDisplay = 2048;
486: try {
487: File repFile = new File(urls.get(index));
488: InputStream is = null;
489: if (repFile.exists()) {
490: is = new FileInputStream(repFile);
491: } else {
492: is = new URL(urls.get(index)).openStream();
493: }
494: if (!table.getParserType().equals(PropertyKeys.SPREADSHEET)) {
495:
496: br = new BufferedReader(new InputStreamReader(is,
497: encoding), maxCharsToRead * 5);
498:
499: StringBuilder strBuf = new StringBuilder(maxCharsToRead);
500: int sz = 0;
501: int ct = 0;
502:
503: char[] charBuf = new char[maxCharsToRead];
504: while ((sz = br.read(charBuf)) != -1
505: && ((ct += sz) < maxCharsToDisplay)) {
506: strBuf.append(charBuf, 0, sz);
507: }
508:
509: record = strBuf.toString();
510:
511: } else {
512: Workbook spreadSheetData = Workbook.getWorkbook(is);
513: Sheet sheet = spreadSheetData.getSheet(table
514: .getProperty("SHEET"));
515: StringBuilder buf = new StringBuilder();
516: for (int i = 0; i < sheet.getRows(); i++) {
517: if (i != 0) {
518: buf.append("\r\n");
519: }
520: Cell[] cells = sheet.getRow(i);
521: for (int j = 0; j < cells.length; j++) {
522: if (j != 0) {
523: buf.append(",");
524: }
525: buf.append(cells[j].getContents());
526: }
527: }
528: record = buf.toString();
529: spreadSheetData.close();
530: }
531: } catch (Exception ioe) {
532: mLogger.errorNoloc(mLoc.t(
533: "PRSR074: Failed to read and parse the file{0}",
534: LOG_CATEGORY), ioe);
535: } finally {
536: if (br != null) {
537: try {
538: br.close();
539: } catch (IOException ignore) {
540: // ignore
541: }
542: }
543:
544: if (record == null) {
545: record = "";
546: }
547: }
548: return record;
549: }
550:
551: public Component getComponent() {
552: if (component == null) {
553: panel = new ParseContentVisualPanel(this );
554: component = (Component) panel;
555: panel.setLayout(new GridBagLayout());
556: parseErrors = null;
557: }
558: return component;
559: }
560:
561: public HelpCtx getHelp() {
562: return HelpCtx.DEFAULT_HELP;
563: }
564:
565: public boolean isValid() {
566: return canAdvance();
567: }
568:
569: private final Set<ChangeListener> listeners = new HashSet<ChangeListener>(
570: 1);
571:
572: public final void addChangeListener(ChangeListener l) {
573: synchronized (listeners) {
574: listeners.add(l);
575: }
576: }
577:
578: public final void removeChangeListener(ChangeListener l) {
579: synchronized (listeners) {
580: listeners.remove(l);
581: }
582: }
583:
584: protected final void fireChangeEvent() {
585: Iterator<ChangeListener> it;
586: synchronized (listeners) {
587: it = new HashSet<ChangeListener>(listeners).iterator();
588: }
589: ChangeEvent ev = new ChangeEvent(this);
590: while (it.hasNext()) {
591: it.next().stateChanged(ev);
592: }
593: }
594: }
|