package com.limegroup.gnutella.gui.xml; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.xml.sax.SAXException; import com.limegroup.gnutella.Assert; import com.limegroup.gnutella.FileDesc; import com.limegroup.gnutella.URN; import com.limegroup.gnutella.gui.LimeTextField; import com.limegroup.gnutella.gui.GUIUtils; import com.limegroup.gnutella.gui.GUIMediator; import com.limegroup.gnutella.xml.LimeXMLDocument; import com.limegroup.gnutella.xml.LimeXMLReplyCollection; import com.limegroup.gnutella.xml.LimeXMLSchema; import com.limegroup.gnutella.xml.LimeXMLSchemaRepository; import com.limegroup.gnutella.xml.LimeXMLUtils; import com.limegroup.gnutella.xml.SchemaNotFoundException; import com.limegroup.gnutella.xml.SchemaReplyCollectionMapper; /** * This Frame is popped up when the user indicated that she is interested in * Editing/viewing meta-data about a particular file. This frame has a one * to one realtionship with files in the library. *<p> * Constructed on the fly when the user selects a file asks to annotate it * The changes made to the meta-data about that file, are committed to disk * as soon as the changes are confirmed. * * @author Sumeet Thadani */ public class MetaEditorFrame extends JDialog implements ActionListener{ private static final String FILE_LABEL=GUIMediator.getStringResource ("META_EDITOR_ANNOTATING_FILE"); private static final int DIALOG_WIDTH = 500; private static final int DIALOG_HEIGHT = 500; private static final int INIT_X = 100; private static final int INIT_Y = 100; private JPanel filePanel; private JLabel fileLabel; private JTextField fileField; private JPanel buttonPanel; private JButton saveButton; private JButton cancelButton; private JButton deleteButton; private JSplitPane splitPane; private JPanel upperLeftPanel; private JPanel lowerLeftPanel; private JList docSchemaList; private JLabel editLabel; private JLabel addLabel; private JList unDocSchemaList; private InputPanel innerEditPanel; private OuterEditingPanel outerEditPanel; private JScrollPane upperLeftScroller; private JScrollPane lowerLeftScroller; private List noDocSchemas;//a list of schemaURIs w/o any docs for this file private List docsOfFile;//stores a list of LimeXMLDocuments private String fileName; private String selectedSchemaURI; private LimeXMLDocument editedDoc; private JSplitPane splitter; private JPanel grandPanel; private URN editFileHashValue; private FileDesc fd; //constructor public MetaEditorFrame(FileDesc fd, String fullName, Frame owner){ super(owner,GUIMediator.getStringResource ("META_EDITOR_ANNOTATING_FILE")+ "\""+fullName+"\"",true); fileName = fullName; this.fd = fd; editFileHashValue = fd.getSHA1Urn(); noDocSchemas = new ArrayList(); docsOfFile = getDocs(); //North panel stuff filePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); fileLabel= new JLabel(FILE_LABEL); fileField = new LimeTextField(); fileField.setText(fullName); fileField.setEditable(false); filePanel.add(fileLabel); filePanel.add(fileField); //South Panel stuff buttonPanel = new JPanel(); //no layout saveButton = new JButton(GUIMediator.getStringResource ("META_EDITOR_SAVE_LABEL")); cancelButton = new JButton(GUIMediator.getStringResource ("META_EDITOR_CANCEL_LABEL")); deleteButton = new JButton(GUIMediator.getStringResource ("META_EDITOR_DELETE_LABEL")); saveButton.addActionListener(this); cancelButton.addActionListener(this); deleteButton.addActionListener(this); buttonPanel.add(saveButton); buttonPanel.add(deleteButton); buttonPanel.add(cancelButton); //West stuff upperLeftPanel = new JPanel(); upperLeftPanel.setLayout(new BorderLayout()); lowerLeftPanel = new JPanel(); lowerLeftPanel.setLayout(new BorderLayout()); editLabel = new JLabel(GUIMediator.getStringResource( "META_EDITOR_EDITING_LABEL")); JPanel p1 = new JPanel(new FlowLayout(FlowLayout.LEFT)); //p1.setBackground(Color.white); p1.add(editLabel); upperLeftPanel.add(p1,BorderLayout.NORTH); int used = docsOfFile.size(); String[] usedSchemas = new String[used]; for(int i=0; i<used; i++){ LimeXMLDocument d = (LimeXMLDocument)docsOfFile.get(i); usedSchemas[i] = XMLUtils.getTitleForSchemaURI(d.getSchemaURI()); } docSchemaList = new JList(usedSchemas); docSchemaList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); docSchemaList.addListSelectionListener(new DocListListener()); upperLeftScroller = new JScrollPane(docSchemaList); upperLeftPanel.add(upperLeftScroller,BorderLayout.CENTER); addLabel = new JLabel(GUIMediator.getStringResource ("META_EDITOR_ADDING_LABEL")); JPanel p2 = new JPanel(new FlowLayout(FlowLayout.LEFT)); //p2.setBackground(Color.white); p2.add(addLabel); lowerLeftPanel.add(p2,BorderLayout.NORTH); int unused = noDocSchemas.size(); String[] unusedSchemas = new String[unused]; for(int j=0;j<unused;j++){ String uri = (String)noDocSchemas.get(j); unusedSchemas[j] = XMLUtils.getTitleForSchemaURI(uri); } unDocSchemaList = new JList(unusedSchemas); unDocSchemaList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); unDocSchemaList.addListSelectionListener(new UnDocListListener()); lowerLeftScroller = new JScrollPane(unDocSchemaList); lowerLeftPanel.add(lowerLeftScroller,BorderLayout.CENTER); splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,upperLeftPanel, lowerLeftPanel); //splitPane.setDividerLocation(0.5); splitPane.setDividerLocation(splitPane.getMinimumDividerLocation()+99); //splitPane.setBackground(Color.white); //East stuff int index = docSchemaList.getSelectedIndex(); if (index>-1){ LimeXMLDocument doc =(LimeXMLDocument)docsOfFile.get(index); String schemaURI = doc.getSchemaURI(); LimeXMLSchemaRepository rep=LimeXMLSchemaRepository.instance(); LimeXMLSchema schema = rep.getSchema(schemaURI); innerEditPanel = new EditingPanel(schema,doc);//it is editable } else{ index = unDocSchemaList.getSelectedIndex(); if(index > -1){ String schemaURI = (String)noDocSchemas.get(index); LimeXMLSchemaRepository rep=LimeXMLSchemaRepository.instance(); LimeXMLSchema schema = rep.getSchema(schemaURI); innerEditPanel=new InputPanel(schema,null,null,null,true,true, false); } } outerEditPanel= new OuterEditingPanel(innerEditPanel); outerEditPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); setSize(DIALOG_WIDTH,DIALOG_HEIGHT); setLocation(INIT_X,INIT_Y); splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,splitPane, outerEditPanel); splitter.setDividerLocation(splitter.getMinimumDividerLocation()); grandPanel = new JPanel(); grandPanel.setLayout(new BorderLayout()); grandPanel.add(filePanel,BorderLayout.NORTH); grandPanel.add(splitter,BorderLayout.CENTER); grandPanel.add(buttonPanel,BorderLayout.SOUTH); JComponent contentPane = (JComponent)getContentPane(); GUIUtils.addHideAction(contentPane); contentPane.add(grandPanel,BorderLayout.CENTER); } public void actionPerformed(ActionEvent event) { if(event.getSource().equals(saveButton)){ boolean close = saveMeta(); if(close) MetaEditorFrame.this.dispose(); } else if(event.getSource().equals(deleteButton)){ removeMeta(); MetaEditorFrame.this.dispose(); } else if(event.getSource().equals(cancelButton)){ MetaEditorFrame.this.dispose(); } } private void removeMeta(){ InputPanel removePanel= outerEditPanel.getContentPanel(); if(!(removePanel instanceof EditingPanel) ){ GUIMediator.showError("ERROR_DEL_META_USER"); return; } String uri = editedDoc.getSchemaURI(); SchemaReplyCollectionMapper map=SchemaReplyCollectionMapper.instance(); LimeXMLReplyCollection collection = map.getReplyCollection(uri); Assert.that(collection!=null, "Trying to remove data from a non-existent collection"); boolean removed = collection.removeDoc(fd); if(removed) editedDoc = null; else {//unable to remove or write to disk GUIMediator.showError("ERROR_DEL_META_SYSTEM"); } } /** * @return the returned boolean decided whether or not to close the dialog */ private boolean saveMeta() { //OK, the save button has been pressed. InputPanel inputPanel = outerEditPanel.getContentPanel(); if (inputPanel == null) return true;//close the dialog String XMLString = inputPanel.getInput();//gets XML string. LimeXMLDocument newDoc = null; LimeXMLDocument oldDoc = null; try{ newDoc = new LimeXMLDocument(XMLString); } catch(SAXException e) { GUIMediator.showError("ERROR_SAVE_META_DOC"); return true;//close save window } catch(SchemaNotFoundException e) { GUIMediator.showError("ERROR_SAVE_META_DOC"); return true; } catch(IOException e) { GUIMediator.showError("ERROR_SAVE_META_DOC"); return true; } //OK we have the new LimeXMLDocument SchemaReplyCollectionMapper map=SchemaReplyCollectionMapper.instance(); String uri = newDoc.getSchemaURI(); LimeXMLReplyCollection collection = map.getReplyCollection(uri); //This is a really bad case! Assert.that(collection!=null, "Can't add document. No Collection exists for uri:"+uri+ "\nDocument created with string: "+XMLString); if(inputPanel instanceof EditingPanel) {//we are editing data oldDoc = collection.replaceDoc(fd, newDoc); } else if(inputPanel instanceof InputPanel) {//we are adding new data collection.addReply(fd,newDoc); } else {//The save button as been pressed in error - do nothing. GUIMediator.showError("ERROR_SAVE_META_ILLEGAL"); } int committed =-1; boolean committed2 = true; if (LimeXMLUtils.isSupportedFormat(fileName)) committed = collection.mediaFileToDisk(fd, fileName, newDoc, false); else committed2 = collection.writeMapToDisk(); if(!committed2){ GUIMediator.showError("ERROR_SAVE_META_DISK"); return true; } switch(committed) { case LimeXMLReplyCollection.FILE_DEFECTIVE: GUIMediator.showError("ERROR_SAVE_META_FILE"); return true; case LimeXMLReplyCollection.RW_ERROR: GUIMediator.showError("ERROR_SAVE_META_RW"); return true; case LimeXMLReplyCollection.BAD_ID3: GUIMediator.showError("ERROR_SAVE_META_ID3"); return true; case LimeXMLReplyCollection.FAILED_TITLE: GUIMediator.showError("ERROR_SAVE_META_ID3"); return false; case LimeXMLReplyCollection.FAILED_ARTIST: cleanUpChanges("audios__audio__artist__",collection,oldDoc); return false; case LimeXMLReplyCollection.FAILED_ALBUM: cleanUpChanges("audios__audio__album__",collection,oldDoc); return false; case LimeXMLReplyCollection.FAILED_YEAR: cleanUpChanges("audios__audio__year__",collection,oldDoc); return false; case LimeXMLReplyCollection.FAILED_COMMENT: cleanUpChanges("audios__audio__comment__",collection,oldDoc); return false; case LimeXMLReplyCollection.FAILED_TRACK: cleanUpChanges("audios__audio__track__",collection,oldDoc); return false; case LimeXMLReplyCollection.FAILED_GENRE: cleanUpChanges("audios__audio__genre__",collection,oldDoc); return false; case LimeXMLReplyCollection.HASH_FAILED: GUIMediator.showError("ERROR_SAVE_META_DISK"); return true; default: return true; } } private void cleanUpChanges(String canonicalFieldName, LimeXMLReplyCollection collection, LimeXMLDocument oldDoc){ GUIMediator.showError("ERROR_SAVE_META_BAD"); JComponent comp=innerEditPanel.getField(canonicalFieldName); innerEditPanel.clearField(comp); if(oldDoc == null)//it was added....just remove collection.removeDoc(fd); else//older one was replaced....replace back collection.replaceDoc(fd, oldDoc); } private List getDocs() { //create a list of LimeXMLDocuments that are associated with this file //first get all the schemas LimeXMLSchemaRepository rep = LimeXMLSchemaRepository.instance(); String[] schemas = rep.getAvailableSchemaURIs(); int len = schemas.length; SchemaReplyCollectionMapper map = SchemaReplyCollectionMapper.instance(); List xmlDocs = new ArrayList(); for(int i=0; i<len;i++){//for each schema LimeXMLReplyCollection coll=map.getReplyCollection(schemas[i]); if(coll == null || coll.getCount()< 1){//null or no data noDocSchemas.add(schemas[i]); continue; } LimeXMLDocument currDoc = coll.getDocForHash(editFileHashValue); if (currDoc==null)//entry not found in this collection noDocSchemas.add(schemas[i]); else xmlDocs.add(currDoc); } return xmlDocs; } private class DocListListener implements ListSelectionListener{ public void valueChanged(ListSelectionEvent event){ int sIndex = unDocSchemaList.getSelectedIndex(); unDocSchemaList.removeSelectionInterval(sIndex,sIndex);//deselect int index = docSchemaList.getSelectedIndex(); if (index>-1){ LimeXMLDocument doc =(LimeXMLDocument)docsOfFile.get(index); MetaEditorFrame.this.editedDoc = doc; MetaEditorFrame.this.selectedSchemaURI = doc.getSchemaURI(); LimeXMLSchemaRepository rep=LimeXMLSchemaRepository.instance(); LimeXMLSchema schema = rep.getSchema(selectedSchemaURI); innerEditPanel=new EditingPanel(schema,doc); outerEditPanel.setContent(innerEditPanel); outerEditPanel.revalidate(); } } } private class UnDocListListener implements ListSelectionListener{ public void valueChanged(ListSelectionEvent event){ int sIndex = docSchemaList.getSelectedIndex(); docSchemaList.removeSelectionInterval(sIndex,sIndex);//deselect int index = unDocSchemaList.getSelectedIndex(); if(index > -1){ String schemaURI = (String)noDocSchemas.get(index); MetaEditorFrame.this.selectedSchemaURI = schemaURI; MetaEditorFrame.this.editedDoc = null; LimeXMLSchemaRepository rep=LimeXMLSchemaRepository.instance(); LimeXMLSchema schema = rep.getSchema(schemaURI); innerEditPanel=new InputPanel(schema,null,null,null, true,true, false); outerEditPanel.setContent(innerEditPanel); outerEditPanel.revalidate(); } } } }