X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FJalview2XML.java;h=4c2b8b643a7cec6b5af4d6495f44ab760c310c3a;hb=345ebd023e398df875d769f85061551888cd71e6;hp=d6061a4354131c1df24ed115ab790e7d8e647a5f;hpb=aa73ca0718f823e32eb7c864e1861dc3372467d5;p=jalview.git diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java old mode 100755 new mode 100644 index d6061a4..4c2b8b6 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -1,1490 +1,5646 @@ /* - * Jalview - A Sequence Alignment Editor and Viewer - * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle - * - * 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 + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview 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 3 * 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. - * + * + * Jalview 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.gui; - -import jalview.schemes.*; - -import jalview.gui.*; - -import java.io.*; - -import java.net.*; - -import java.util.*; - -import java.util.jar.*; - -import javax.swing.*; - -import org.exolab.castor.xml.*; - -import jalview.schemabinding.version2.*; - - - +import jalview.analysis.Conservation; +import jalview.api.FeatureColourI; +import jalview.api.ViewStyleI; +import jalview.api.structures.JalviewStructureDisplayI; +import jalview.bin.Cache; +import jalview.datamodel.AlignedCodonFrame; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.GraphLine; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.RnaViewerModel; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.datamodel.StructureViewerModel; +import jalview.datamodel.StructureViewerModel.StructureData; +import jalview.ext.varna.RnaModel; +import jalview.gui.StructureViewer.ViewerType; +import jalview.io.DataSourceType; +import jalview.io.FileFormat; +import jalview.renderer.ResidueShaderI; +import jalview.schemabinding.version2.AlcodMap; +import jalview.schemabinding.version2.AlcodonFrame; +import jalview.schemabinding.version2.Annotation; +import jalview.schemabinding.version2.AnnotationColours; +import jalview.schemabinding.version2.AnnotationElement; +import jalview.schemabinding.version2.CalcIdParam; +import jalview.schemabinding.version2.DBRef; +import jalview.schemabinding.version2.Features; +import jalview.schemabinding.version2.Group; +import jalview.schemabinding.version2.HiddenColumns; +import jalview.schemabinding.version2.JGroup; +import jalview.schemabinding.version2.JSeq; +import jalview.schemabinding.version2.JalviewModel; +import jalview.schemabinding.version2.JalviewModelSequence; +import jalview.schemabinding.version2.MapListFrom; +import jalview.schemabinding.version2.MapListTo; +import jalview.schemabinding.version2.Mapping; +import jalview.schemabinding.version2.MappingChoice; +import jalview.schemabinding.version2.OtherData; +import jalview.schemabinding.version2.PdbentryItem; +import jalview.schemabinding.version2.Pdbids; +import jalview.schemabinding.version2.Property; +import jalview.schemabinding.version2.RnaViewer; +import jalview.schemabinding.version2.SecondaryStructure; +import jalview.schemabinding.version2.Sequence; +import jalview.schemabinding.version2.SequenceSet; +import jalview.schemabinding.version2.SequenceSetProperties; +import jalview.schemabinding.version2.Setting; +import jalview.schemabinding.version2.StructureState; +import jalview.schemabinding.version2.ThresholdLine; +import jalview.schemabinding.version2.Tree; +import jalview.schemabinding.version2.UserColours; +import jalview.schemabinding.version2.Viewport; +import jalview.schemes.AnnotationColourGradient; +import jalview.schemes.ColourSchemeI; +import jalview.schemes.ColourSchemeProperty; +import jalview.schemes.FeatureColour; +import jalview.schemes.ResidueProperties; +import jalview.schemes.UserColourScheme; +import jalview.structure.StructureSelectionManager; +import jalview.structures.models.AAStructureBindingModel; +import jalview.util.MessageManager; +import jalview.util.Platform; +import jalview.util.StringUtils; +import jalview.util.jarInputStreamProvider; +import jalview.viewmodel.AlignmentViewport; +import jalview.viewmodel.ViewportRanges; +import jalview.viewmodel.seqfeatures.FeatureRendererSettings; +import jalview.viewmodel.seqfeatures.FeaturesDisplayed; +import jalview.ws.jws2.Jws2Discoverer; +import jalview.ws.jws2.dm.AAConSettings; +import jalview.ws.jws2.jabaws2.Jws2Instance; +import jalview.ws.params.ArgumentI; +import jalview.ws.params.AutoCalcSetting; +import jalview.ws.params.WsParamSetI; + +import java.awt.Color; +import java.awt.Rectangle; +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Vector; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; + +import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; + +import org.exolab.castor.xml.Marshaller; +import org.exolab.castor.xml.Unmarshaller; /** - * DOCUMENT ME! - * + * Write out the current jalview desktop state as a Jalview XML stream. + * + * Note: the vamsas objects referred to here are primitive versions of the + * VAMSAS project schema elements - they are not the same and most likely never + * will be :) + * * @author $author$ - * @version $Revision$ + * @version $Revision: 1.134 $ */ public class Jalview2XML { - // SAVES SEVERAL ALIGNEMENT WINDOWS TO SAME JARFILE - public void SaveState(File statefile) + private static final String VIEWER_PREFIX = "viewer_"; + + private static final String RNA_PREFIX = "rna_"; + + private static final String UTF_8 = "UTF-8"; + + // use this with nextCounter() to make unique names for entities + private int counter = 0; + + /* + * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps + * of sequence objects are created. + */ + IdentityHashMap seqsToIds = null; + + /** + * jalview XML Sequence ID to jalview sequence object reference (both dataset + * and alignment sequences. Populated as XML reps of sequence objects are + * created.) + */ + Map seqRefIds = null; + + Map incompleteSeqs = null; + + List frefedSequence = null; + + boolean raiseGUI = true; // whether errors are raised in dialog boxes or not + + /* + * Map of reconstructed AlignFrame objects that appear to have come from + * SplitFrame objects (have a dna/protein complement view). + */ + private Map splitFrameCandidates = new HashMap<>(); + + /* + * Map from displayed rna structure models to their saved session state jar + * entry names + */ + private Map rnaSessions = new HashMap<>(); + + /** + * create/return unique hash string for sq + * + * @param sq + * @return new or existing unique string for sq + */ + String seqHash(SequenceI sq) + { + if (seqsToIds == null) { - long creation = System.currentTimeMillis(); - JInternalFrame[] frames = Desktop.desktop.getAllFrames(); + initSeqRefs(); + } + if (seqsToIds.containsKey(sq)) + { + return seqsToIds.get(sq); + } + else + { + // create sequential key + String key = "sq" + (seqsToIds.size() + 1); + key = makeHashCode(sq, key); // check we don't have an external reference + // for it already. + seqsToIds.put(sq, key); + return key; + } + } - if (frames == null) - { - return; - } + void clearSeqRefs() + { + if (_cleartables) + { + if (seqRefIds != null) + { + seqRefIds.clear(); + } + if (seqsToIds != null) + { + seqsToIds.clear(); + } + if (incompleteSeqs != null) + { + incompleteSeqs.clear(); + } + // seqRefIds = null; + // seqsToIds = null; + } + else + { + // do nothing + warn("clearSeqRefs called when _cleartables was not set. Doing nothing."); + // seqRefIds = new Hashtable(); + // seqsToIds = new IdentityHashMap(); + } + } - try - { - FileOutputStream fos = new FileOutputStream(statefile); - JarOutputStream jout = new JarOutputStream(fos); + void initSeqRefs() + { + if (seqsToIds == null) + { + seqsToIds = new IdentityHashMap<>(); + } + if (seqRefIds == null) + { + seqRefIds = new HashMap<>(); + } + if (incompleteSeqs == null) + { + incompleteSeqs = new HashMap<>(); + } + if (frefedSequence == null) + { + frefedSequence = new ArrayList<>(); + } + } + + public Jalview2XML() + { + } + + public Jalview2XML(boolean raiseGUI) + { + this.raiseGUI = raiseGUI; + } + + /** + * base class for resolving forward references to sequences by their ID + * + * @author jprocter + * + */ + abstract class SeqFref + { + String sref; + + String type; + + public SeqFref(String _sref, String type) + { + sref = _sref; + this.type = type; + } - //NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS - //////////////////////////////////////////////////// - PrintWriter out = new PrintWriter(new OutputStreamWriter(jout, - "UTF-8")); + public String getSref() + { + return sref; + } - Vector shortNames = new Vector(); + public SequenceI getSrefSeq() + { + return seqRefIds.get(sref); + } - //REVERSE ORDER - for (int i = frames.length - 1; i > -1; i--) - { - if (frames[i] instanceof AlignFrame) - { - AlignFrame af = (AlignFrame) frames[i]; + public boolean isResolvable() + { + return seqRefIds.get(sref) != null; + } - String shortName = af.getTitle(); + public SequenceI getSrefDatasetSeq() + { + SequenceI sq = seqRefIds.get(sref); + if (sq != null) + { + while (sq.getDatasetSequence() != null) + { + sq = sq.getDatasetSequence(); + } + } + return sq; + } - if (shortName.indexOf(File.separatorChar) > -1) - { - shortName = shortName.substring(shortName.lastIndexOf( - File.separatorChar) + 1); - } + /** + * @return true if the forward reference was fully resolved + */ + abstract boolean resolve(); - int count = 1; + @Override + public String toString() + { + return type + " reference to " + sref; + } + } + + /** + * create forward reference for a mapping + * + * @param sref + * @param _jmap + * @return + */ + public SeqFref newMappingRef(final String sref, + final jalview.datamodel.Mapping _jmap) + { + SeqFref fref = new SeqFref(sref, "Mapping") + { + public jalview.datamodel.Mapping jmap = _jmap; - while (shortNames.contains(shortName)) - { - if (shortName.endsWith("_" + (count - 1))) - { - shortName = shortName.substring(0, - shortName.lastIndexOf("_")); - } + @Override + boolean resolve() + { + SequenceI seq = getSrefDatasetSeq(); + if (seq == null) + { + return false; + } + jmap.setTo(seq); + return true; + } + }; + return fref; + } - shortName = shortName.concat("_" + count); - count++; - } + public SeqFref newAlcodMapRef(final String sref, + final AlignedCodonFrame _cf, + final jalview.datamodel.Mapping _jmap) + { - shortNames.addElement(shortName); + SeqFref fref = new SeqFref(sref, "Codon Frame") + { + AlignedCodonFrame cf = _cf; - if (!shortName.endsWith(".xml")) - { - shortName = shortName + ".xml"; - } + public jalview.datamodel.Mapping mp = _jmap; - SaveState(af, creation, shortName, jout, out); - } - } + @Override + public boolean isResolvable() + { + return super.isResolvable() && mp.getTo() != null; + }; - out.close(); - jout.close(); + @Override + boolean resolve() + { + SequenceI seq = getSrefDatasetSeq(); + if (seq == null) + { + return false; } - catch (Exception ex) + cf.addMap(seq, mp.getTo(), mp.getMap()); + return true; + } + }; + return fref; + } + + public void resolveFrefedSequences() + { + Iterator nextFref = frefedSequence.iterator(); + int toresolve = frefedSequence.size(); + int unresolved = 0, failedtoresolve = 0; + while (nextFref.hasNext()) + { + SeqFref ref = nextFref.next(); + if (ref.isResolvable()) + { + try + { + if (ref.resolve()) + { + nextFref.remove(); + } + else + { + failedtoresolve++; + } + } catch (Exception x) { - ex.printStackTrace(); + System.err.println( + "IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence " + + ref.getSref()); + x.printStackTrace(); + failedtoresolve++; } + } + else + { + unresolved++; + } } - - // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW - public void SaveAlignment(AlignFrame af, String jarFile, - String fileName) + if (unresolved > 0) { - try + System.err.println("Jalview Project Import: There were " + unresolved + + " forward references left unresolved on the stack."); + } + if (failedtoresolve > 0) + { + System.err.println("SERIOUS! " + failedtoresolve + + " resolvable forward references failed to resolve."); + } + if (incompleteSeqs != null && incompleteSeqs.size() > 0) + { + System.err.println( + "Jalview Project Import: There are " + incompleteSeqs.size() + + " sequences which may have incomplete metadata."); + if (incompleteSeqs.size() < 10) + { + for (SequenceI s : incompleteSeqs.values()) { - FileOutputStream fos = new FileOutputStream(jarFile); - JarOutputStream jout = new JarOutputStream(fos); + System.err.println(s.toString()); + } + } + else + { + System.err.println( + "Too many to report. Skipping output of incomplete sequences."); + } + } + } - //NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS - //////////////////////////////////////////////////// - PrintWriter out = new PrintWriter(new OutputStreamWriter(jout, - "UTF-8")); + /** + * This maintains a map of viewports, the key being the seqSetId. Important to + * set historyItem and redoList for multiple views + */ + Map viewportsAdded = new HashMap<>(); - SaveState(af, System.currentTimeMillis(), fileName, jout, out); - out.close(); - jout.close(); - } - catch (Exception ex) + Map annotationIds = new HashMap<>(); + + String uniqueSetSuffix = ""; + + /** + * List of pdbfiles added to Jar + */ + List pdbfiles = null; + + // SAVES SEVERAL ALIGNMENT WINDOWS TO SAME JARFILE + public void saveState(File statefile) + { + FileOutputStream fos = null; + try + { + fos = new FileOutputStream(statefile); + JarOutputStream jout = new JarOutputStream(fos); + saveState(jout); + + } catch (Exception e) + { + // TODO: inform user of the problem - they need to know if their data was + // not saved ! + if (errorMessage == null) + { + errorMessage = "Couldn't write Jalview Archive to output file '" + + statefile + "' - See console error log for details"; + } + else + { + errorMessage += "(output file was '" + statefile + "')"; + } + e.printStackTrace(); + } finally + { + if (fos != null) + { + try + { + fos.close(); + } catch (IOException e) { - ex.printStackTrace(); + // ignore } + } } - - /** - * DOCUMENT ME! - * - * @param af DOCUMENT ME! - * @param timeStamp DOCUMENT ME! - * @param fileName DOCUMENT ME! - * @param jout DOCUMENT ME! - * @param out DOCUMENT ME! + reportErrors(); + } + + /** + * Writes a jalview project archive to the given Jar output stream. + * + * @param jout + */ + public void saveState(JarOutputStream jout) + { + AlignFrame[] frames = Desktop.getAlignFrames(); + + if (frames == null) + { + return; + } + saveAllFrames(Arrays.asList(frames), jout); + } + + /** + * core method for storing state for a set of AlignFrames. + * + * @param frames + * - frames involving all data to be exported (including containing + * splitframes) + * @param jout + * - project output stream + */ + private void saveAllFrames(List frames, JarOutputStream jout) + { + Hashtable dsses = new Hashtable<>(); + + /* + * ensure cached data is clear before starting */ - public void SaveState(AlignFrame af, long timeStamp, - String fileName, JarOutputStream jout, PrintWriter out) + // todo tidy up seqRefIds, seqsToIds initialisation / reset + rnaSessions.clear(); + splitFrameCandidates.clear(); + + try { - Vector seqids = new Vector(); - Vector userColours = new Vector(); - AlignViewport av = af.viewport; + // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS + // ////////////////////////////////////////////////// - JalviewModel object = new JalviewModel(); - object.setVamsasModel(new jalview.schemabinding.version2.VamsasModel()); + List shortNames = new ArrayList<>(); + List viewIds = new ArrayList<>(); + + // REVERSE ORDER + for (int i = frames.size() - 1; i > -1; i--) + { + AlignFrame af = frames.get(i); + // skip ? + if (skipList != null && skipList + .containsKey(af.getViewport().getSequenceSetId())) + { + continue; + } - object.setCreationDate(new java.util.Date(timeStamp)); - object.setVersion(jalview.bin.Cache.getProperty("VERSION")); + String shortName = makeFilename(af, shortNames); - jalview.datamodel.AlignmentI jal = af.viewport.alignment; - jalview.datamodel.AlignmentI jalhidden = null; + int ap, apSize = af.alignPanels.size(); - if(av.hasHiddenRows) + for (ap = 0; ap < apSize; ap++) { - jalhidden = jal; - jal = jal.getHiddenSequences().getFullAlignment(); - } + AlignmentPanel apanel = af.alignPanels.get(ap); + String fileName = apSize == 1 ? shortName : ap + shortName; + if (!fileName.endsWith(".xml")) + { + fileName = fileName + ".xml"; + } + + saveState(apanel, fileName, jout, viewIds); + String dssid = getDatasetIdRef( + af.getViewport().getAlignment().getDataset()); + if (!dsses.containsKey(dssid)) + { + dsses.put(dssid, af); + } + } + } - SequenceSet vamsasSet = new SequenceSet(); - Sequence vamsasSeq; - JalviewModelSequence jms = new JalviewModelSequence(); + writeDatasetFor(dsses, "" + jout.hashCode() + " " + uniqueSetSuffix, + jout); - vamsasSet.setGapChar(jal.getGapCharacter() + ""); + try + { + jout.flush(); + } catch (Exception foo) + { + } + ; + jout.close(); + } catch (Exception ex) + { + // TODO: inform user of the problem - they need to know if their data was + // not saved ! + if (errorMessage == null) + { + errorMessage = "Couldn't write Jalview Archive - see error output for details"; + } + ex.printStackTrace(); + } + } + + /** + * Generates a distinct file name, based on the title of the AlignFrame, by + * appending _n for increasing n until an unused name is generated. The new + * name (without its extension) is added to the list. + * + * @param af + * @param namesUsed + * @return the generated name, with .xml extension + */ + protected String makeFilename(AlignFrame af, List namesUsed) + { + String shortName = af.getTitle(); + + if (shortName.indexOf(File.separatorChar) > -1) + { + shortName = shortName + .substring(shortName.lastIndexOf(File.separatorChar) + 1); + } - JSeq jseq; - Vector pdbfiles = null; + int count = 1; - //SAVE SEQUENCES - int id = 0; - for (int i = 0; i < jal.getHeight(); i++) - { - seqids.add(jal.getSequenceAt(i)); - vamsasSeq = new Sequence(); - vamsasSeq.setId(id + ""); - vamsasSeq.setName(jal.getSequenceAt(i).getName()); - vamsasSeq.setSequence(jal.getSequenceAt(i).getSequence()); - vamsasSeq.setDescription(jal.getSequenceAt(i).getDescription()); + while (namesUsed.contains(shortName)) + { + if (shortName.endsWith("_" + (count - 1))) + { + shortName = shortName.substring(0, shortName.lastIndexOf("_")); + } - if(jal.getSequenceAt(i).getDatasetSequence().getDBRef()!=null) - { - jalview.datamodel.DBRefEntry [] dbrefs = - jal.getSequenceAt(i).getDatasetSequence().getDBRef(); + shortName = shortName.concat("_" + count); + count++; + } - for(int d=0; d frames = new ArrayList<>(); - jseq.setId(id); + // resolve splitframes + if (af.getViewport().getCodingComplement() != null) + { + frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames(); + } + else + { + frames.add(af); + } + saveAllFrames(frames, jout); + try + { + jout.flush(); + } catch (Exception foo) + { + } + ; + jout.close(); + return true; + } catch (Exception ex) + { + errorMessage = "Couldn't Write alignment view to Jalview Archive - see error output for details"; + ex.printStackTrace(); + return false; + } + } - if (av.hasHiddenRows) - { - jseq.setHidden(jalhidden.getHiddenSequences().isHidden( - jal.getSequenceAt(i))); + private void writeDatasetFor(Hashtable dsses, + String fileName, JarOutputStream jout) + { - if(jal.getSequenceAt(i).getHiddenSequences()!=null) - { - jalview.datamodel.SequenceI [] reps = - jal.getSequenceAt(i).getHiddenSequences().getSequencesInOrder(jal); + for (String dssids : dsses.keySet()) + { + AlignFrame _af = dsses.get(dssids); + String jfileName = fileName + " Dataset for " + _af.getTitle(); + if (!jfileName.endsWith(".xml")) + { + jfileName = jfileName + ".xml"; + } + saveState(_af.alignPanel, jfileName, true, jout, null); + } + } + + /** + * create a JalviewModel from an alignment view and marshall it to a + * JarOutputStream + * + * @param ap + * panel to create jalview model for + * @param fileName + * name of alignment panel written to output stream + * @param jout + * jar output stream + * @param viewIds + * @param out + * jar entry name + */ + public JalviewModel saveState(AlignmentPanel ap, String fileName, + JarOutputStream jout, List viewIds) + { + return saveState(ap, fileName, false, jout, viewIds); + } + + /** + * create a JalviewModel from an alignment view and marshall it to a + * JarOutputStream + * + * @param ap + * panel to create jalview model for + * @param fileName + * name of alignment panel written to output stream + * @param storeDS + * when true, only write the dataset for the alignment, not the data + * associated with the view. + * @param jout + * jar output stream + * @param out + * jar entry name + */ + public JalviewModel saveState(AlignmentPanel ap, String fileName, + boolean storeDS, JarOutputStream jout, List viewIds) + { + if (viewIds == null) + { + viewIds = new ArrayList<>(); + } - for(int h=0; h userColours = new ArrayList<>(); - if(jal.getSequenceAt(i).getDatasetSequence().getSequenceFeatures()!=null) - { - jalview.datamodel.SequenceFeature[] sf - = jal.getSequenceAt(i).getDatasetSequence().getSequenceFeatures(); - int index = 0; - while(index < sf.length) - { - Features features = new Features(); - - features.setBegin(sf[index].getBegin()); - features.setEnd(sf[index].getEnd()); - features.setDescription(sf[index].getDescription()); - features.setType(sf[index].getType()); - features.setFeatureGroup(sf[index].getFeatureGroup()); - features.setScore(sf[index].getScore()); - if(sf[index].links!=null) - { - for(int l=0; l