44764db439a872510de274500ccda446153ca556
[jalview.git] / src / jalview / structure / StructureCommandsBase.java
1 package jalview.structure;
2
3 import jalview.api.AlignViewportI;
4 import jalview.api.AlignmentViewPanel;
5 import jalview.api.FeatureRenderer;
6 import jalview.api.SequenceRenderer;
7 import jalview.datamodel.AlignmentI;
8 import jalview.datamodel.HiddenColumns;
9 import jalview.datamodel.SequenceI;
10 import jalview.renderer.seqfeatures.FeatureColourFinder;
11 import jalview.util.Comparison;
12
13 import java.awt.Color;
14 import java.util.ArrayList;
15 import java.util.LinkedHashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19
20 /**
21  * A base class holding methods useful to all classes that implement commands
22  * for structure viewers
23  * 
24  * @author gmcarstairs
25  *
26  */
27 public abstract class StructureCommandsBase implements StructureCommandsI
28 {
29   private static final String CMD_SEPARATOR = ";";
30
31   /**
32    * Returns something that separates concatenated commands
33    * 
34    * @return
35    */
36   protected static String getCommandSeparator()
37   {
38     return CMD_SEPARATOR;
39   }
40
41   @Override
42   public String[] setAttributesForFeatures(StructureSelectionManager ssm,
43           String[] files, SequenceI[][] sequence, AlignmentViewPanel avp)
44   {
45     // default does nothing, override where this is implemented
46     return null;
47   }
48
49   /**
50    * Returns the lowest model number used by the structure viewer
51    * 
52    * @return
53    */
54   @Override
55   public int getModelStartNo()
56   {
57     return 0;
58   }
59
60   /**
61    * Helper method to add one contiguous range to the AtomSpec model for the given
62    * value (creating the model if necessary). As used by Jalview, {@code value} is
63    * <ul>
64    * <li>a colour, when building a 'colour structure by sequence' command</li>
65    * <li>a feature value, when building a 'set Chimera attributes from features'
66    * command</li>
67    * </ul>
68    * 
69    * @param map
70    * @param value
71    * @param model
72    * @param startPos
73    * @param endPos
74    * @param chain
75    */
76   public static final void addAtomSpecRange(Map<Object, AtomSpecModel> map,
77           Object value,
78           int model, int startPos, int endPos, String chain)
79   {
80     /*
81      * Get/initialize map of data for the colour
82      */
83     AtomSpecModel atomSpec = map.get(value);
84     if (atomSpec == null)
85     {
86       atomSpec = new AtomSpecModel();
87       map.put(value, atomSpec);
88     }
89   
90     atomSpec.addRange(model, startPos, endPos, chain);
91   }
92
93   /**
94    * Traverse the map of colours/models/chains/positions to construct a list of
95    * 'color' commands (one per distinct colour used). The format of each command
96    * is specific to the structure viewer.
97    * 
98    * @param colourMap
99    * @return
100    */
101   @Override
102   public String[] colourBySequence(Map<Object, AtomSpecModel> colourMap)
103   {
104     /*
105      * This version concatenates all commands into a single String (semi-colon
106      * delimited). If length limit issues arise, refactor to return one color
107      * command per colour.
108      */
109     List<String> commands = new ArrayList<>();
110     StringBuilder sb = new StringBuilder(256);
111     boolean firstColour = true;
112     for (Object key : colourMap.keySet())
113     {
114       Color colour = (Color) key;
115       if (!firstColour)
116       {
117         sb.append(getCommandSeparator()).append(" ");
118       }
119       firstColour = false;
120       final AtomSpecModel colourData = colourMap.get(colour);
121       sb.append(getColourCommand(colourData, colour));
122     }
123     commands.add(sb.toString());
124
125     return commands.toArray(new String[commands.size()]);
126   }
127
128   /**
129    * Returns a command to colour the atoms represented by {@code atomSpecModel}
130    * with the colour specified by {@code colourCode}.
131    * 
132    * @param atomSpecModel
133    * @param colour
134    * @return
135    */
136   protected String getColourCommand(AtomSpecModel atomSpecModel, Color colour)
137   {
138     String atomSpec = getAtomSpec(atomSpecModel, false);
139     return getColourCommand(atomSpec, colour);
140   }
141
142   /**
143    * Returns a command to colour the atoms described (in viewer command syntax)
144    * by {@code atomSpec} with the colour specified by {@code colourCode}
145    * 
146    * @param atomSpec
147    * @param colour
148    * @return
149    */
150   protected abstract String getColourCommand(String atomSpec, Color colour);
151
152   @Override
153   public String colourByResidues(Map<String, Color> colours)
154   {
155     StringBuilder cmd = new StringBuilder(12 * colours.size());
156   
157     for (Entry<String, Color> entry : colours.entrySet())
158     {
159       String residue = entry.getKey();
160       String atomSpec = getResidueSpec(residue);
161       cmd.append(getColourCommand(atomSpec, entry.getValue()));
162       cmd.append(getCommandSeparator());
163     }
164     return cmd.toString();
165   }
166
167   /**
168    * Helper method to append one start-end range to an atomspec string
169    * 
170    * @param sb
171    * @param start
172    * @param end
173    * @param chain
174    * @param firstPositionForModel
175    */
176   protected void appendRange(StringBuilder sb, int start, int end,
177           String chain, boolean firstPositionForModel, boolean isChimeraX)
178   {
179     if (!firstPositionForModel)
180     {
181       sb.append(",");
182     }
183     if (end == start)
184     {
185       sb.append(start);
186     }
187     else
188     {
189       sb.append(start).append("-").append(end);
190     }
191
192     if (!isChimeraX)
193     {
194       sb.append(".");
195       if (!" ".equals(chain))
196       {
197         sb.append(chain);
198       }
199     }
200   }
201
202   /**
203    * Returns the atom specifier meaning all occurrences of the given residue
204    * 
205    * @param residue
206    * @return
207    */
208   protected abstract String getResidueSpec(String residue);
209 }