package timeflow.app.ui; import timeflow.data.time.*; import timeflow.data.db.*; import timeflow.format.field.*; import timeflow.format.file.DelimitedFormat; import timeflow.model.*; import timeflow.util.*; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.text.ParseException; import java.util.*; import java.io.*; import java.net.MalformedURLException; import java.net.URL; public class ImportDelimitedPanel extends JFrame { String fileName; SchemaPanel schemaPanel; boolean exitOnClose=false; // for testing! JScrollPane scroller; TFModel model; JLabel numLinesLabel=new JLabel(); // for testing: public static void main(String[] args) throws Exception { System.out.println("Starting test of ImportEditor"); String file="data/probate.tsv"; String[][] data=DelimitedFormat.readArrayGuessDelim(file, System.out); ImportDelimitedPanel editor=new ImportDelimitedPanel(new TFModel()); editor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); editor.setFileName(file); editor.setData(data); editor.setBounds(50,50,900,800); editor.setVisible(true); editor.exitOnClose=true; } public ImportDelimitedPanel(final TFModel model) { super("Import File"); this.model=model; setBackground(Color.white); setLayout(new BorderLayout()); JPanel top=new JPanel(); add(top, BorderLayout.NORTH); top.setLayout(new FlowLayout(FlowLayout.LEFT)); top.setBackground(Color.lightGray); top.add(numLinesLabel); final JTextField source=new JTextField(12); JButton done=new JButton("Import This"); top.add(done); done.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { model.setDB(schemaPanel.makeDB(source.getText()), fileName, true, this); setVisible(false); if (exitOnClose) System.exit(0); }}); JButton cancel=new JButton("Cancel"); top.add(cancel); cancel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { setVisible(false); if (exitOnClose) System.exit(0); }}); top.add(new JLabel(" Enter A Source:")); top.add(source); schemaPanel=new SchemaPanel(); schemaPanel.setBackground(Color.white); scroller=new JScrollPane(schemaPanel); add(scroller, BorderLayout.CENTER); } public void scrollToTop() { scroller.getViewport().setViewPosition(new Point(0,0)); } public void setFileName(String fileName) { this.fileName=fileName; } public void setData(String[][] data) { numLinesLabel.setText((data.length-1)+" records read. "); schemaPanel.display(data); } class SchemaPanel extends JPanel { int numFields, rows; String[][] data; ArrayList<FieldPanel> panels=new ArrayList<FieldPanel>(); ActDB makeDB(String source) { // count number of fields that are not ignored. int n=0; for (FieldPanel fp: panels) if (!fp.ignore.isSelected()) n++; Class[] types=new Class[n]; String[] fieldNames=new String[n]; int[] index=new int[n]; if (source.trim().length()==0) source="[source unspecified]"; int i=0, j=0; for (FieldPanel fp: panels) { if (!fp.ignore.isSelected()) { fieldNames[i]=fp.fieldName; String typeChoice=(String)fp.typeChoices.getSelectedItem(); Class type=FieldFormatCatalog.javaClass(typeChoice); System.out.println("Type: "+type+" for: "+typeChoice+" from "+fp.fieldName); types[i]=type; index[i]=j; i++; } j++; } ActDB db= new ArrayDB(fieldNames, types, source); HashMap<Integer, StringBuffer> errors=new HashMap<Integer, StringBuffer>(); for (i=1; i<data.length; i++) { Act act=db.createAct(); for (int k=0; k<n; k++) { j=index[k]; String s=data[i][j]; Field f=db.getField(fieldNames[k]); FieldFormat format=FieldFormatCatalog.getFormat(types[k]); try { Object o=format.parse(s); act.set(f,o); } catch (Exception e) { StringBuffer b=errors.get(i-1); if (b==null) { b=new StringBuffer(); errors.put(i-1,b); } else b.append("; "); b.append(f.getName()+":"+s); } } } if (errors.size()>0) { Field error=db.addField("UNPARSED FIELDS", String.class); for (int row:errors.keySet()) { db.get(row).set(error, errors.get(row).toString()); } } for (j=0; j<n; j++) { System.out.println(db.getField(fieldNames[j])); } return db; } void display(String[][] data) { removeAll(); this.data=data; // analyze data. Class[] guesses=FieldFormatGuesser.analyze(data, 1, 100); // go through first row, which is headers. String[] headers=data[0]; // if there are duplicate headers, add indicators. HashSet<String> h=new HashSet<String>(); for (int i=0; i<headers.length; i++) { String base=headers[i]; int j=2; String name=base; while (h.contains(name)) { name=base+" "+j; j++; } headers[i]=name; h.add(name); } numFields=headers.length; int cols=2; rows=(int)Math.ceil(numFields/2.0); setLayout(new GridLayout(rows, cols)); for (int i=0; i<numFields; i++) { Bag<String> vals=new Bag<String>(); for (int j=1; j<data.length; j++) { vals.add(data[j][i]); } java.util.List<String> top=vals.listTop(5); int n=top.size(); String[] samples=new String[n]; for (int j=0; j<n; j++) { String s=top.get(j); samples[j]=(s.length()==0 ? "*MISSING*" : s)+" ("+vals.num(s)+" times)"; } JPanel p=new JPanel(); add(p); p.setLayout(new BorderLayout()); FieldPanel f=new FieldPanel(headers[i], samples, guesses[i]); panels.add(f); p.add(f, BorderLayout.CENTER); JPanel hr=new JPanel(); hr.setPreferredSize(new Dimension(20,20)); p.add(hr, BorderLayout.SOUTH); } } public Dimension getPreferredSize() { return new Dimension(400,150*rows); } } class FieldPanel extends JPanel { JComboBox typeChoices; String fieldName; JCheckBox ignore; JLabel fieldLabel; int x1=5, y1=20, y3=150,x2=150, x3=150, x4=375, dh=2; FieldPanel(String fieldName, String[] sampleValues, Class typeGuess) { // just going with a null layout here, because it's a lot simpler! setLayout(null); setBackground(Color.white); this.fieldName=fieldName; fieldLabel=new JLabel(" \""+fieldName+'"'); fieldLabel.setFont(model.getDisplay().big()); add(fieldLabel); fieldLabel.setBounds(x1,y1,fieldLabel.getPreferredSize().width, fieldLabel.getPreferredSize().height); typeChoices=new JComboBox(); for (String choice: FieldFormatCatalog.classNames()) typeChoices.addItem(choice); typeChoices.setSelectedItem(FieldFormatCatalog.humanName(typeGuess)); add(typeChoices); int y2=fieldLabel.getY()+fieldLabel.getHeight()+dh+5; typeChoices.setBounds(x1,y2, typeChoices.getPreferredSize().width, typeChoices.getPreferredSize().height); ignore=new JCheckBox("Ignore Field"); add(ignore); ignore.setBounds(x1,typeChoices.getY()+typeChoices.getHeight()+dh, ignore.getPreferredSize().width, ignore.getPreferredSize().height); ignore.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Color c=ignore.isSelected() ? Color.gray : Color.black; fieldLabel.setForeground(c); typeChoices.setForeground(c); }}); JTextArea values=new JTextArea(); values.setForeground(Color.gray); for (int i=0; i<sampleValues.length; i++) values.append(sampleValues[i]+"\n"); add(values); values.setBounds(x3,y2,x4-x3,y3-y2); } public Dimension getPreferredSize() { return new Dimension(500,150); } } }