/*- * #%L * Fiji distribution of ImageJ for the life sciences. * %% * Copyright (C) 2007 - 2017 Fiji developers. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. * #L% */ package spim.fiji.plugin.queryXML; import fiji.util.gui.GenericDialogPlus; import ij.gui.GenericDialog; import java.awt.Color; import java.awt.Label; import java.awt.TextField; import java.awt.event.ActionListener; import java.awt.event.TextEvent; import java.awt.event.TextListener; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import javax.management.RuntimeErrorException; import mpicbg.spim.data.SpimDataException; import mpicbg.spim.data.Version; import mpicbg.spim.data.XmlKeys; import mpicbg.spim.data.generic.AbstractSpimData; import mpicbg.spim.data.generic.XmlIoAbstractSpimData; import mpicbg.spim.data.generic.base.Entity; import mpicbg.spim.data.generic.base.NamedEntity; import mpicbg.spim.data.generic.sequence.AbstractSequenceDescription; import mpicbg.spim.data.generic.sequence.BasicImgLoader; import mpicbg.spim.data.generic.sequence.BasicViewDescription; import mpicbg.spim.data.generic.sequence.BasicViewSetup; import mpicbg.spim.data.sequence.Angle; import mpicbg.spim.data.sequence.Channel; import mpicbg.spim.data.sequence.Illumination; import mpicbg.spim.data.sequence.TimePoint; import mpicbg.spim.io.IOFunctions; import spim.fiji.plugin.Toggle_Cluster_Options; import spim.fiji.plugin.util.GUIHelper; import spim.fiji.spimdata.EmptyEntity; import spim.fiji.spimdata.NamePattern; /** * Interface for interactive parsing of spimdata XMLs * * @author Stephan Preibisch (stephan.preibisch@gmx.de) * * @param <AS> * @param <S> * @param <V> * @param <D> * @param <L> */ public class GenericLoadParseQueryXML< AS extends AbstractSpimData< S >, S extends AbstractSequenceDescription< V, D, L >, V extends BasicViewSetup, D extends BasicViewDescription< V >, L extends BasicImgLoader, X extends XmlIoAbstractSpimData< S, AS > > { public static String defaultXMLfilename = ""; public static boolean debugRandomClusterHash = false; protected static String goodMsg1 = "The selected XML file was parsed successfully"; protected static String warningMsg1 = "The selected file does not appear to be an xml. Press OK to try to parse anyways."; protected static String errorMsg1 = "An ERROR occured parsing this XML file! Please select a different XML (see log)"; protected static String neutralMsg1 = "No XML file selected."; protected static String noMsg2 = " "; protected static String[] attributeChoiceList = new String[]{ "All *s", "Single * (Select from List)", "Multiple *s (Select from List)", "Range of *s (Specify by Name)" }; public static final String[] clusterOptions1 = new String[]{ "Do not process on cluster", "Save every XML with unique id generated from processed subset", "Save every XML with user-provided unique id" }; public static int defaultClusterOption1 = 1; // remember as default public static HashMap< String, Integer > defaultAttributeChoice = new HashMap< String, Integer >(); public static HashMap< String, Integer > defaultAttributeEntityIndex = new HashMap< String, Integer >(); public static HashMap< String, String > defaultAttributePatternString = new HashMap< String, String >(); public static HashMap< String, boolean[] > defaultAttributeSelectedEntities = new HashMap< String, boolean[] >(); // the current instance protected HashMap< String, Integer > attributeChoice = new HashMap< String, Integer >(); // how to load the XML final protected X io; // how to sort the attributes (if not by name) protected Comparator< String > comparator = null; // local variables for display protected String message1, message2; protected Color color; // result variables protected AS data; protected String xmlfilename; protected ArrayList< TimePoint > timepointsToProcess; // which attributes are there protected ArrayList< String > attributes; // all instances of all entities per attribute protected HashMap< String, List< Entity > > allAttributeInstances; // all instances of all entities per attribute protected HashMap< String, List< Entity > > attributeInstancesToProcess; // extension for the XML when saving protected String clusterExt = null; // add a button on demand protected String buttonText = null; protected ActionListener listener = null; protected GenericDialog gd = null; protected boolean returnfalse = false; /** * Constructor for the class needs an appropriate IO module * @param io */ public GenericLoadParseQueryXML( final X io ) { this.io = io; IOFunctions.println( "Using spimdata version: " + Version.getVersion() ); IOFunctions.println( "Using spimreconstruction version: " + spim.Version.getVersion() ); } /** * @return the i/o object used to parse the XML */ public X getIO() { return io; } /** * @return the SpimData object parsed from the xml */ public AS getData() { return data; } /** * @return The location of the xml file */ public String getXMLFileName() { return xmlfilename; } /** * @return All timepoints that should be processed */ public List< TimePoint > getTimePointsToProcess() { return timepointsToProcess; } /** * @return All ViewSetups that should be processed per timepoint (based on the selected attributes) */ public ArrayList< V > getViewSetupsToProcess() { final ArrayList< V > viewSetups = new ArrayList< V >(); for ( final V v : data.getSequenceDescription().getViewSetupsOrdered() ) { boolean contained = true; // check over all attributes if they are part of the attributeInstancesToProcess for ( final String attribute : attributes ) { final Entity attributeEntityForViewSetup = v.getAttributes().get( attribute ); if ( !attributeInstancesToProcess.get( attribute ).contains( attributeEntityForViewSetup ) ) contained = false; } if ( contained ) viewSetups.add( v ); } return viewSetups; } /** * Sets the comparator used to sort the Attributes in a specific order for display, can be null * Note that Timepoint (XmlKeys.TIMEPOINTS_TIMEPOINT_TAG) is always the last entry no matter what as it is special * @param comparator */ public void setComparator( final Comparator< String > comparator ) { this.comparator = comparator; } public Comparator< String > getComparator() { return comparator; } public boolean queryXML() { return queryXML( "", "Process", null ); } public boolean queryXML( final List< String > specifyAttributes ) { return queryXML( "", "Process", specifyAttributes ); } public boolean queryXML( final String query ) { return queryXML( "", query, null ); } public boolean queryXML( final String query, final List< String > specifyAttributes ) { return queryXML( "", query, specifyAttributes ); } public void addButton( final String buttonText, final ActionListener listener ) { this.buttonText = buttonText; this.listener = listener; } public GenericDialog getGenericDialog() { return gd; } public void setReturnFalse( final boolean value ) { this.returnfalse = value; } /** * Asks the user for a valid XML (real time parsing) * * @param specifyAttributes - set of attributes the user should be asked if he/she wants to select a subset of them * @return null if cancelled or timepointlistsize = 0 */ public boolean queryXML( final String additionalTitle, String query, List< String > specifyAttributes ) { // should not be null, just empty if ( specifyAttributes == null ) specifyAttributes = new ArrayList< String >(); // they are ordered by alphabet (or user defined) so that the details and then queried in the same order if ( comparator == null ) Collections.sort( specifyAttributes ); else Collections.sort( specifyAttributes, comparator ); // timepoint is always last if ( specifyAttributes.contains( XmlKeys.TIMEPOINTS_TIMEPOINT_TAG ) ) { specifyAttributes.remove( XmlKeys.TIMEPOINTS_TIMEPOINT_TAG ); specifyAttributes.add( XmlKeys.TIMEPOINTS_TIMEPOINT_TAG ); } // adjust query to support recording if ( query.contains( " " ) ) query = query.replaceAll( " ", "_" ); this.attributeChoice = new HashMap< String, Integer >(); // try parsing if it ends with XML tryParsing( defaultXMLfilename, false ); final GenericDialogPlus gd; if ( additionalTitle != null && additionalTitle.length() > 0 ) gd = new GenericDialogPlus( "Select dataset for " + additionalTitle ); else gd = new GenericDialogPlus( "Select Dataset" ); gd.addFileField( "Select_XML", defaultXMLfilename, 65 ); gd.addMessage( this.message1, GUIHelper.largestatusfont, this.color ); Label l1 = (Label)gd.getMessage(); gd.addMessage( this.message2, GUIHelper.smallStatusFont, this.color ); Label l2 = (Label)gd.getMessage(); if ( specifyAttributes.size() > 0 ) gd.addMessage( " " ); for ( int i = 0; i < specifyAttributes.size(); ++i ) { final String attribute = specifyAttributes.get( i ); final String[] choices = makeChoiceList( attribute ); final int defaultChoice = defaultAttributeChoice.containsKey( attribute ) ? defaultAttributeChoice.get( attribute ) : 0; gd.addChoice( query + "_" + attribute, choices, choices[ defaultChoice ] ); } if ( Toggle_Cluster_Options.displayClusterProcessing ) { gd.addMessage( "" ); gd.addChoice( "XML_Output", clusterOptions1, clusterOptions1[ defaultClusterOption1 ] ); gd.addMessage( "Note: Later on you need to merge the different XML's using Plugins>MultiView Reconstruction>Tools>Cluster>Merge Cluster Jobs", GUIHelper.smallStatusFont ); } addListeners( gd, (TextField)gd.getStringFields().firstElement(), l1, l2 ); if ( buttonText != null && listener != null ) { gd.addMessage( "" ); gd.addMessage( "OR" ); gd.addMessage( "" ); gd.addButton( buttonText, listener ); this.gd = gd; } gd.addMessage( "" ); GUIHelper.addPreibischLabWebsite( gd ); gd.showDialog(); if ( gd.wasCanceled() || returnfalse ) return false; String xmlFilename = defaultXMLfilename = gd.getNextString(); // try to parse the file anyways tryParsing( xmlFilename, true ); for ( int i = 0; i < specifyAttributes.size(); ++i ) { final String attribute = specifyAttributes.get( i ); final int choice = gd.getNextChoiceIndex(); defaultAttributeChoice.put( attribute, choice ); attributeChoice.put( attribute, choice ); } final int clusterSaving; // check for cluster options if selected if ( Toggle_Cluster_Options.displayClusterProcessing ) clusterSaving = defaultClusterOption1 = gd.getNextChoiceIndex(); else clusterSaving = 0; // fill up angles, channels, illuminations, timepoints (all, if there is no further dialog) if ( !queryDetails() ) return false; if ( clusterSaving == 0 ) { this.clusterExt = ""; } else if ( clusterSaving == 1 ) { this.clusterExt = "job_" + createUniqueName(); } else { final GenericDialog gdCluster = new GenericDialog( "Define unique ID" ); gdCluster.addStringField( "UNIQUE_ID", "" ); gdCluster.addMessage( "Note: Using an ID twice might result in overwriting of the XML files.", GUIHelper.smallStatusFont ); gdCluster.showDialog(); if ( gdCluster.wasCanceled() ) return false; this.clusterExt = "job_" + gdCluster.getNextString(); } return true; } public String getClusterExtension() { return this.clusterExt; } protected String createUniqueName() { long idSum = 1; for ( final TimePoint t : getTimePointsToProcess() ) idSum *= t.getId(); for ( final BasicViewSetup v : getViewSetupsToProcess() ) idSum += v.getId(); long nano = System.nanoTime(); long millis = System.currentTimeMillis(); long finalHash = nano + millis + idSum; if ( debugRandomClusterHash ) { IOFunctions.println( "idsum=" + idSum ); IOFunctions.println( "nano=" + nano ); IOFunctions.println( "millis=" + millis ); IOFunctions.println( "final=" + finalHash ); } return "" + finalHash; } /** * Querys a single element from the list * * @param name - type of elements (e.g. "Timepoint") * @param list - list of available elements * @param defaultSelection - default selection * @return the selection or -1 if cancelled */ public static int queryIndividualEntry( final String name, final String[] list, int defaultSelection ) { if ( defaultSelection >= list.length ) defaultSelection = 0; final GenericDialog gd = new GenericDialog( "Select Single " + name ); gd.addChoice( "Processing_" + name, list, list[ defaultSelection ] ); gd.showDialog(); if ( gd.wasCanceled() ) return -1; return gd.getNextChoiceIndex(); } /** * Querys a multiple element from the list * * @param name - type of elements (e.g. "Timepoints") * @param list - list of available elements * @param defaultSelection - default selection * @return the selection or null if cancelled */ public static boolean[] queryMultipleEntries( final String name, final String[] list, boolean[] defaultSelection ) { if ( defaultSelection == null || defaultSelection.length != list.length ) { defaultSelection = new boolean[ list.length ]; defaultSelection[ 0 ] = true; for ( int i = 1; i < list.length; ++i ) defaultSelection[ i ] = false; // by default select first two if ( defaultSelection.length > 1 ) defaultSelection[ 1 ] = true; } for ( int i = 0; i < list.length; ++i ) list[ i ] = list[ i ].replace( " ", "_" ); final GenericDialog gd = new GenericDialog( "Select Multiple " + name ); gd.addMessage( "" ); for ( int i = 0; i < list.length; ++i ) gd.addCheckbox( list[ i ], defaultSelection[ i ] ); gd.addMessage( "" ); GUIHelper.addScrollBars( gd ); gd.showDialog(); if ( gd.wasCanceled() ) return null; for ( int i = 0; i < list.length; ++i ) { if ( gd.getNextBoolean() ) defaultSelection[ i ] = true; else defaultSelection[ i ] = false; } return defaultSelection; } /** * Querys a pattern of element from the list * * @param name - type of elements (e.g. "Timepoints") * @param list - list of available elements * @param defaultSelectionArray - default selection (array of size 1 to be able to return it) * @return the selection or null if cancelled */ public static boolean[] queryPattern( final String name, final String[] list, final String[] defaultSelectionArray ) { String defaultSelection = defaultSelectionArray[ 0 ]; if ( defaultSelection == null || defaultSelection.length() == 0 ) { defaultSelection = list[ 0 ]; for ( int i = 1; i < Math.min( list.length, 3 ); ++i ) defaultSelection += "," + list[ i ]; } final GenericDialog gd = new GenericDialog( "Select Range of " + name ); gd.addMessage( "" ); gd.addStringField( "Process_following_" + name, defaultSelection, 30 ); gd.addMessage( "" ); gd.addMessage( "Available " + name + ":" ); final String singular = name.substring( 0, name.length() - 1 ) + " "; String allTps = singular + list[ 0 ]; for ( int i = 1; i < list.length; ++i ) allTps += "\n" + singular + list[ i ]; gd.addMessage( allTps, GUIHelper.smallStatusFont ); GUIHelper.addScrollBars( gd ); gd.showDialog(); if ( gd.wasCanceled() ) return null; // the result final boolean[] selected = new boolean[ list.length ]; for ( int i = 0; i < list.length; ++i ) selected[ i ] = false; try { final ArrayList< String > nameList = NamePattern.parseNameString( defaultSelection = gd.getNextString(), true ); for ( final String entry : nameList ) { boolean found = false; for ( int i = 0; i < list.length && !found; ++i ) { if ( entry.equals( list[ i ] ) ) { selected[ i ] = true; found = true; } } if ( !found ) IOFunctions.println( name + " " + entry + " not part of the list of " + name + "s. Ignoring it." ); } } catch ( final ParseException e ) { IOFunctions.println( "Cannot parse pattern '" + defaultSelection + "': " + e ); return null; } defaultSelectionArray[ 0 ] = defaultSelection; return selected; } protected < E extends Entity > boolean query( final int choice, final String attribute, final List< E > allEntities, final List< E > entitiesToProcess ) { if ( choice == 1 ) // choose a single entry { int defaultSelection = defaultAttributeEntityIndex.containsKey( attribute ) ? defaultAttributeEntityIndex.get( attribute ) : 0; if ( defaultSelection >= allEntities.size() ) defaultSelection = 0; final int selection = queryIndividualEntry( attribute, buildEntityList( attribute, allEntities, true ), defaultSelection ); if ( selection >= 0 ) entitiesToProcess.add( allEntities.get( selection ) ); else return false; defaultAttributeEntityIndex.put( attribute, selection ); } else if ( choice == 2 || choice == 3 ) // choose multiple angles or angles defined by pattern { final boolean[] selection; String[] defaultPattern = new String[]{ defaultAttributePatternString.get( attribute ) }; boolean[] defaultSelectedEntities = defaultAttributeSelectedEntities.get( attribute ); if ( choice == 2 ) selection = queryMultipleEntries( attribute + "s", buildEntityList( attribute, allEntities, true ), defaultSelectedEntities ); else selection = queryPattern( attribute + "s", buildEntityList( attribute, allEntities, false ), defaultPattern ); if ( selection == null ) return false; else { defaultAttributeSelectedEntities.put( attribute, selection ); if ( choice == 3 ) defaultAttributePatternString.put( attribute, defaultPattern[ 0 ] ); for ( int i = 0; i < selection.length; ++i ) if ( selection[ i ] ) entitiesToProcess.add( allEntities.get( i ) ); } } else { entitiesToProcess.addAll( allEntities ); } if ( entitiesToProcess.size() == 0 ) { throw new RuntimeException( "List of " + attribute + "s is empty. Stopping." ); //IOFunctions.println( "WARNING: List of " + attribute + "s is empty." ); //return true; } else { String selected = ""; for ( int e = 0; e < entitiesToProcess.size(); ++e ) { if ( entitiesToProcess.get( e ) instanceof NamedEntity ) selected += ((NamedEntity) entitiesToProcess.get( e )).getName(); else selected += entitiesToProcess.get( e ).getId(); if ( e != entitiesToProcess.size() - 1 ) selected += ", "; } IOFunctions.println( attribute + "s selected: " + selected ); } return true; } protected boolean queryDetails() { // all attibutes this.attributeInstancesToProcess = new HashMap< String, List< Entity > >(); for ( int attributeIndex = 0; attributeIndex < this.attributes.size(); ++attributeIndex ) this.attributeInstancesToProcess.put( this.attributes.get( attributeIndex ), new ArrayList< Entity >() ); for ( int i = 0; i < this.attributes.size(); ++i ) { final String attribute = this.attributes.get( i ); final int choice = attributeChoice.containsKey( attribute ) ? attributeChoice.get( attribute ) : 0; if ( !query( choice, attribute, this.allAttributeInstances.get( attribute ), this.attributeInstancesToProcess.get( attribute ) ) ) return false; } // timepoints this.timepointsToProcess = new ArrayList< TimePoint >(); final String attribute = XmlKeys.TIMEPOINTS_TIMEPOINT_TAG; final int choice = attributeChoice.containsKey( attribute ) ? attributeChoice.get( attribute ) : 0; if ( !query( choice, attribute, this.data.getSequenceDescription().getTimePoints().getTimePointsOrdered(), this.timepointsToProcess ) ) return false; return true; } protected static String[] buildTimepointList( final List< TimePoint > tpList, final boolean addTitle ) { final String[] timepoints = new String[ tpList.size() ]; for ( int i = 0; i < timepoints.length; ++i ) if ( addTitle ) timepoints[ i ] = "Timepoint " + tpList.get( i ).getName(); else timepoints[ i ] = tpList.get( i ).getName(); return timepoints; } protected static String[] buildEntityList( final String attributeName, final List< ? extends Entity > entityList, final boolean addTitle ) { final String[] entities = new String[ entityList.size() ]; for ( int i = 0; i < entities.length; ++i ) { final Entity e = entityList.get( i ); final String entityName; if ( e instanceof NamedEntity ) entityName = ((NamedEntity)e).getName(); else entityName = Integer.toString( e.getId() ); if ( addTitle ) entities[ i ] = attributeName + " " + entityName; else entities[ i ] = entityName; } return entities; } protected static String[] makeChoiceList( final String attribute ) { final String[] choiceList = new String[ attributeChoiceList.length ]; for ( int i = 0; i < choiceList.length; ++i ) choiceList[ i ] = attributeChoiceList[ i ].replace( "*", attribute ); return choiceList; } protected boolean tryParsing( final String xmlfile, final boolean parseAllTypes ) { this.xmlfilename = xmlfile; this.message1 = neutralMsg1; this.message2 = noMsg2; this.color = GUIHelper.neutral; this.data = null; if ( parseAllTypes || ( !parseAllTypes && xmlfile.endsWith( ".xml" ) ) ) { try { this.data = parseXML( xmlfile ); // which attributes this.attributes = new ArrayList< String >(); this.attributes.addAll( this.data.getSequenceDescription().getViewSetupsOrdered().get( 0 ).getAttributes().keySet() ); // the attributes are ordered by alphabet (or user defined) so that the details and then queried in the same order if ( comparator == null ) Collections.sort( this.attributes ); else Collections.sort( this.attributes, comparator ); // get the list of entity instances per attribute final int numAttributes = this.attributes.size(); this.allAttributeInstances = new HashMap< String, List< Entity > >(); // count number of entity id's per attribute and make sure we add them only once final ArrayList< HashSet< Integer > > numEntitiesPerAttrib = new ArrayList< HashSet< Integer > >( numAttributes ); for ( int attributeIndex = 0; attributeIndex < numAttributes; ++attributeIndex ) { this.allAttributeInstances.put( this.attributes.get( attributeIndex ), new ArrayList< Entity >() ); numEntitiesPerAttrib.add( new HashSet< Integer >() ); } for ( final V viewSetup : this.data.getSequenceDescription().getViewSetupsOrdered() ) { for ( int attributeIndex = 0; attributeIndex < numAttributes; ++attributeIndex ) { final String attribute = this.attributes.get( attributeIndex ); // the number of id's per attribute final HashSet< Integer > numEntityIds = numEntitiesPerAttrib.get( attributeIndex ); // the entity instance (could be Angle, Channel, etc ... ) Entity e = viewSetup.getAttributes().get( attribute ); if ( e == null ) { if ( attribute.equals( "angle" ) ) { IOFunctions.println( new Date( System.currentTimeMillis() ) + ": 'angle' attribute undefined, using Angle 0 to support it." ); e = new Angle( 0 ); viewSetup.setAttribute( e ); } else if ( attribute.equals( "channel" ) ) { IOFunctions.println( new Date( System.currentTimeMillis() ) + ": 'channel' attribute undefined, using Channel 0 to support it." ); e = new Channel( 0 ); viewSetup.setAttribute( e ); } else if ( attribute.equals( "illumination" ) ) { IOFunctions.println( new Date( System.currentTimeMillis() ) + ": 'illumination' attribute undefined, using Illumination 0 to support it." ); e = new Illumination( 0 ); viewSetup.setAttribute( e ); } else { // something new we do not know IOFunctions.println( new Date( System.currentTimeMillis() ) + ": Unknown entity '" + attribute + "', adding placeholder entity to support it." ); e = new EmptyEntity( 0, attribute ); viewSetup.getAttributes().put( attribute, e ); } } final int id = e.getId(); if ( !numEntityIds.contains( id ) ) { numEntityIds.add( id ); this.allAttributeInstances.get( attribute ).add( e ); } } } // sort all entity lists for ( final String attribute : this.allAttributeInstances.keySet() ) Collections.sort( this.allAttributeInstances.get( attribute ), new Comparator<Entity>() { @Override public int compare( final Entity o1, final Entity o2 ) { return o1.getId() - o2.getId(); } }); int countMissingViews = 0; for ( final D v : this.data.getSequenceDescription().getViewDescriptions().values() ) if ( !v.isPresent() ) ++countMissingViews; final int timepoints = this.data.getSequenceDescription().getTimePoints().size(); this.message1 = goodMsg1; this.message2 = ""; for ( int attributeIndex = 0; attributeIndex < numAttributes; ++attributeIndex ) { final int numEntities = numEntitiesPerAttrib.get( attributeIndex ).size(); String entityName = this.attributes.get( attributeIndex ); if ( numEntities > 1 ) entityName += "s"; this.message2 += numEntities + " " + entityName + ", "; } if ( timepoints > 1 ) this.message2 += timepoints + " timepoints, "; else this.message2 += timepoints + " timepoint, "; if ( countMissingViews == 1 ) this.message2 += countMissingViews + " missing view"; else this.message2 += countMissingViews + " missing views"; this.color = GUIHelper.good; } catch ( final Exception e ) { this.message1 = errorMsg1; this.message2 = noMsg2; this.color = GUIHelper.error; IOFunctions.println( "Cannot parse '" + xmlfile + "': " + e ); e.printStackTrace(); return false; } } else if ( xmlfile.length() > 0 ) { this.message1 = warningMsg1; this.message2 = noMsg2; this.color = GUIHelper.warning; return false; } return true; } protected AS parseXML( final String xmlFilename ) throws SpimDataException { return io.load( xmlFilename ); } protected void addListeners( final GenericDialog gd, final TextField tf, final Label label1, final Label label2 ) { final GenericLoadParseQueryXML< ?,?,?,?,?,? > lpq = this; // using TextListener instead tf.addTextListener( new TextListener() { @Override public void textValueChanged( final TextEvent t ) { if ( t.getID() == TextEvent.TEXT_VALUE_CHANGED ) { final String xmlFilename = tf.getText(); // try parsing if it ends with XML tryParsing( xmlFilename, false ); label1.setText( lpq.message1 ); label2.setText( lpq.message2 ); label1.setForeground( lpq.color ); label2.setForeground( lpq.color ); } } }); } }