/* * EuroCarbDB, a framework for carbohydrate bioinformatics * * Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * A copy of this license accompanies this distribution in the file LICENSE.txt. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * Last commit: $Rev: 1930 $ by $Author: david@nixbioinf.org $ on $Date:: 2010-07-29 #$ */ package org.eurocarbdb.application.glycanbuilder; import java.awt.*; import java.io.*; import java.util.*; import javax.swing.*; import javax.swing.table.*; import javax.swing.event.*; /** Base class for all document type objects. @author Alessio Ceroni (a.ceroni@imperial.ac.uk) */ public abstract class BaseDocument { /** Listener for document events. */ public interface DocumentChangeListener { /** Called when the document is initialized. */ public void documentInit(DocumentChangeEvent e); /** Called when some part of the document has changed. */ public void documentChanged(DocumentChangeEvent e); } /** Base class for document events. */ public static class DocumentChangeEvent extends java.util.EventObject { protected BaseDocument theDoc; /** Default constructor, set the event source to the changed document. */ public DocumentChangeEvent(BaseDocument _theDoc) { super(_theDoc); theDoc = _theDoc; } } // undo/redo protected GlycanUndoManager theUndoManager = null; // file handling protected String filename = ""; protected boolean was_saved = false; protected boolean has_changed = false; // events protected Vector<DocumentChangeListener> dc_listeners = new Vector<DocumentChangeListener>(); //---------------- /** Empty constructor. @see #init */ public BaseDocument() { init(); theUndoManager = new GlycanUndoManager(this); this.components=new HashMap<String,Component>(); } /** Empty constructor. @param undo_redo if <code>true</code> provides undo/redo facilities for this document @see #init @see GlycanUndoManager */ public BaseDocument(boolean undo_redo) { init(); this.components=new HashMap<String,Component>(); if( undo_redo ) theUndoManager = new GlycanUndoManager(this); } //---------------- DATA ACCESS ----------------- /** Return the size of the document. Implementation dependent. */ abstract public int size(); /** Return <code>true</code> if the size of the document is 0. */ public boolean isEmpty() { return (size()==0); } /** Return the name of the document. Implementation dependent. */ abstract public String getName(); /** Return the icon associated with the document. The default value is a default empty document icon. */ public javax.swing.ImageIcon getIcon() { try { return FileUtils.themeManager.getImageIcon("basedoc",ICON_SIZE.SMALL); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } /** Return the path to the file from which the document has been loaded or where the document has been saved. Return "[untitled]" otherwise. */ public String getFileName() { return filename; } /** Return an object representing the file associated with the document, or null otherwise. @see #getFileName */ public File getFile() { if( filename!=null && filename.length()>0 && was_saved ) return new File(filename); return null; } /** Return the list of file formats accepted by this document. */ abstract public Collection<javax.swing.filechooser.FileFilter> getFileFormats(); /** Return the list of file formats accepted by this document as a single filter. */ abstract public javax.swing.filechooser.FileFilter getAllFileFormats(); /** Return <code>true</code> if the document was saved to file. */ public boolean wasSaved() { return was_saved; } /** Return <code>true</code> if the document has changed from the last initialization. */ public boolean hasChanged() { return has_changed; } /** Return the undo/redo manager. */ public GlycanUndoManager getUndoManager() { return theUndoManager; } //--------------- // initialization /** Reset <code>filename</code>, <code>saved</code> and <code>changed</code> flags. */ public void resetStatus() { filename = "[untitled]"; was_saved = false; has_changed = false; } /** Set the name of the file associated with the document. Set the <code>save</code> flag and reset the <code>changed</code> flag. */ public void setFilename(String filename) { this.filename = filename; this.was_saved = true; this.has_changed = false; } /** Manually set the <code>changed</code> flag. */ public void setChanged(boolean changed) { has_changed = changed; } /** Initialize the document and reset the status. */ public void init() { // source resetStatus(); // init data initData(); // fire event fireDocumentInit(); } /** Initialize the document by parsing it from a string. @see #fromString */ public boolean init(String value) { try { // source resetStatus(); // init data initData(); fromString(value); // fire event fireDocumentInit(); return true; } catch( Exception e ) { LogUtils.report(e); return false; } } protected boolean fill(String value) { return fill(value,false); } protected boolean fill(String value, boolean initial_state) { try { // fromString(value); // fire event if( initial_state ) fireDocumentRestored(); else fireDocumentChanged(); return true; } catch( Exception e ) { LogUtils.report(e); return false; } } /** Empty the current document. */ public void clear() { initData(); fireDocumentChanged(); } /** Implementation dependent part of the document initialization process. */ abstract public void initData(); /** Read the document from a file. @param merge if <code>true</code> the content of the file is appended to the current document, when possible @param warning if <code>true</true> report when the file cannot be parsed @see #read */ public boolean open(String filename, boolean merge, boolean warning) { System.err.println("In open1"); return open(new File(filename), merge, warning); } /** Read the document from a file. @param merge if <code>true</code> the content of the file is appended to the current document, when possible @param warning if <code>true</true> report when the file cannot be parsed @see #read */ public boolean open(File file, boolean merge, boolean warning) { try { FileInputStream fis = new FileInputStream(file); System.err.println("In open two"); // read structure try { read(fis,merge); } catch(Exception e) { System.err.println("Got exception: "+e.getMessage()); init(); if( warning ) throw e; return false; } // setFilename(file.getAbsolutePath()); fireDocumentInit(); return true; } catch( Exception e ) { LogUtils.report(e); return false; } } protected void read(InputStream is, boolean merge) throws Exception { System.err.println("in read"); BufferedReader br = new BufferedReader(new InputStreamReader(is)); fromString(consume(br),merge); } protected String consume(BufferedReader br) throws Exception { StringBuilder ret = new StringBuilder(); int ch; while( (ch = br.read())!=-1 ) ret.appendCodePoint(ch); return ret.toString(); } /** Save the document to a file. @see #write */ public boolean save(String filename) { try{ // write to tmp file File tmpfile = File.createTempFile("gwb",null); write(new FileOutputStream(tmpfile)); // copy to dest file and delete tmp file FileUtils.copy(tmpfile,new File(filename)); tmpfile.delete(); // setFilename(filename); // fireDocumentInit(); return true; } catch( Exception e ) { LogUtils.report(e); return false; } } /** */ public void write(OutputStream os) throws Exception { BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os)); String str = toString(); bw.write(str,0,str.length()); bw.newLine(); bw.close(); } //--------------- // events /** Register a new listener of document change events. */ public void addDocumentChangeListener(DocumentChangeListener l) { if( l!=null && !dc_listeners.contains(l) ) dc_listeners.add(l); } /** Remove an existing listener of document change events. */ public void removeDocumentChangeListener(DocumentChangeListener l) { if( l!=null ) dc_listeners.remove(l); } /** Send a document init event to all listeners and reset the <code>changed</code> flag. */ public void fireDocumentInit() { has_changed = false; for( DocumentChangeListener dcl : dc_listeners ) dcl.documentInit(new DocumentChangeEvent(this)); } /** Send a document init event to all listeners with a different source and reset the <code>changed</code> flag. */ public void fireDocumentInit(BaseDocument source) { has_changed = false; for( DocumentChangeListener dcl : dc_listeners ) dcl.documentInit(new DocumentChangeEvent(source)); } /** Send a document changed event and reset the <code>changed</code> flag. */ public void fireDocumentRestored() { has_changed = false; for( DocumentChangeListener dcl : dc_listeners ) dcl.documentChanged(new DocumentChangeEvent(this)); } /** Send a document changed event and set the <code>changed</code> flag. */ public void fireDocumentChanged() { has_changed = true; for( DocumentChangeListener dcl : dc_listeners ) dcl.documentChanged(new DocumentChangeEvent(this)); } /** Send a document changed event with a different source and set the <code>changed</code> flag. */ public void fireDocumentChanged(BaseDocument source) { has_changed = true; for( DocumentChangeListener dcl : dc_listeners ) dcl.documentChanged(new DocumentChangeEvent(source)); } //--------------- // serialization /** Parse the document from a string. Implementation dependent. @throws Exception on parsing errors */ public void fromString(String str) throws Exception { fromString(str,false); } /** Parse the document from a string. Implementation dependent. @param merge if <code>true</code> append the content of the string to the document, when possible @throws Exception on parsing errors */ abstract public void fromString(String str, boolean merge) throws Exception; /** * Some components are causing jittering motion when they are painted from scratch, * seems to only occur when calling setIcon(). As a temporary solution components * associated with documents can be registered here - so they aren't redrawn from * scratch. */ HashMap<String,Component> components; public void registerComponent(String id,Component component){ components.put(id,component); } public Component getRegisteredComponent(String id){ return components.get(id); } }