JAL-4265 JAL-4267 added a ‘restoreSession’ StructureViewer.getBinding() method that...
[jalview.git] / src / jalview / structure / StructureCommandsBase.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.structure;
22
23 import java.awt.Color;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28
29 import jalview.bin.Console;
30
31 /**
32  * A base class holding methods useful to all classes that implement commands
33  * for structure viewers
34  * 
35  * @author gmcarstairs
36  *
37  */
38 public abstract class StructureCommandsBase implements StructureCommandsI
39 {
40   public static final String NAMESPACE_PREFIX = "jv_";
41
42   private static final String CMD_SEPARATOR = ";";
43
44   /**
45    * Returns something that separates concatenated commands
46    * 
47    * @return
48    */
49   protected String getCommandSeparator()
50   {
51     return CMD_SEPARATOR;
52   }
53
54   /**
55    * Returns the lowest model number used by the structure viewer
56    * 
57    * @return
58    */
59   @Override
60   public int getModelStartNo()
61   {
62     return 0;
63   }
64
65   /**
66    * Helper method to add one contiguous range to the AtomSpec model for the
67    * given value (creating the model if necessary). As used by Jalview,
68    * {@code value} is
69    * <ul>
70    * <li>a colour, when building a 'colour structure by sequence' command</li>
71    * <li>a feature value, when building a 'set Chimera attributes from features'
72    * command</li>
73    * </ul>
74    * 
75    * @param map
76    * @param value
77    * @param model
78    * @param startPos
79    * @param endPos
80    * @param chain
81    */
82   public static final void addAtomSpecRange(Map<Object, AtomSpecModel> map,
83           Object value, String model, int startPos, int endPos,
84           String chain)
85   {
86     /*
87      * Get/initialize map of data for the colour
88      */
89     AtomSpecModel atomSpec = map.get(value);
90     if (atomSpec == null)
91     {
92       atomSpec = new AtomSpecModel();
93       map.put(value, atomSpec);
94     }
95
96     atomSpec.addRange(model, startPos, endPos, chain);
97   }
98
99   /**
100    * Makes a structure viewer attribute name for a Jalview feature type by
101    * prefixing it with "jv_", and replacing any non-alphanumeric characters with
102    * an underscore
103    * 
104    * @param featureType
105    * @return
106    */
107   protected String makeAttributeName(String featureType)
108   {
109     StringBuilder sb = new StringBuilder();
110     if (featureType != null)
111     {
112       for (char c : featureType.toCharArray())
113       {
114         sb.append(Character.isLetterOrDigit(c) ? c : '_');
115       }
116     }
117     String attName = NAMESPACE_PREFIX + sb.toString();
118     return attName;
119   }
120
121   /**
122    * Traverse the map of colours/models/chains/positions to construct a list of
123    * 'color' commands (one per distinct colour used). The format of each command
124    * is specific to the structure viewer.
125    * <p>
126    * The default implementation returns a single command containing one command
127    * per colour, concatenated.
128    * 
129    * @param colourMap
130    * @return
131    */
132   @Override
133   public List<StructureCommandI> colourBySequence(
134           Map<Object, AtomSpecModel> colourMap)
135   {
136     List<StructureCommandI> commands = new ArrayList<>();
137     StringBuilder sb = new StringBuilder(colourMap.size() * 20);
138     boolean first = true;
139     for (Object key : colourMap.keySet())
140     {
141       Color colour = (Color) key;
142       final AtomSpecModel colourData = colourMap.get(colour);
143       StructureCommandI command = getColourCommand(colourData, colour);
144       if (!first)
145       {
146         sb.append(getCommandSeparator());
147       }
148       first = false;
149       sb.append(command.getCommand());
150     }
151
152     commands.add(new StructureCommand(sb.toString()));
153     return commands;
154   }
155
156   /**
157    * Returns a command to colour the atoms represented by {@code atomSpecModel}
158    * with the colour specified by {@code colourCode}.
159    * 
160    * @param atomSpecModel
161    * @param colour
162    * @return
163    */
164   protected StructureCommandI getColourCommand(AtomSpecModel atomSpecModel,
165           Color colour)
166   {
167     String atomSpec = getAtomSpec(atomSpecModel, AtomSpecType.RESIDUE_ONLY);
168     return colourResidues(atomSpec, colour);
169   }
170
171   /**
172    * Returns a command to colour the atoms described (in viewer command syntax)
173    * by {@code atomSpec} with the colour specified by {@code colourCode}
174    * 
175    * @param atomSpec
176    * @param colour
177    * @return
178    */
179   protected abstract StructureCommandI colourResidues(String atomSpec,
180           Color colour);
181
182   @Override
183   public List<StructureCommandI> colourByResidues(
184           Map<String, Color> colours)
185   {
186     List<StructureCommandI> commands = new ArrayList<>();
187     for (Entry<String, Color> entry : colours.entrySet())
188     {
189       commands.add(colourResidue(entry.getKey(), entry.getValue()));
190     }
191     return commands;
192   }
193
194   private StructureCommandI colourResidue(String resName, Color col)
195   {
196     String atomSpec = getResidueSpec(resName);
197     return colourResidues(atomSpec, col);
198   }
199
200   /**
201    * Helper method to append one start-end range to an atomspec string
202    * 
203    * @param sb
204    * @param start
205    * @param end
206    * @param chain
207    * @param firstPositionForModel
208    */
209   protected void appendRange(StringBuilder sb, int start, int end,
210           String chain, boolean firstPositionForModel, boolean isChimeraX)
211   {
212     if (!firstPositionForModel)
213     {
214       sb.append(",");
215     }
216     if (end == start)
217     {
218       sb.append(start);
219     }
220     else
221     {
222       sb.append(start).append("-").append(end);
223     }
224
225     if (!isChimeraX)
226     {
227       sb.append(".");
228       if (!" ".equals(chain))
229       {
230         sb.append(chain);
231       }
232     }
233   }
234
235   /**
236    * Returns the atom specifier meaning all occurrences of the given residue
237    * 
238    * @param residue
239    * @return
240    */
241   protected abstract String getResidueSpec(String residue);
242
243   @Override
244   public List<StructureCommandI> setAttributes(
245           Map<String, Map<Object, AtomSpecModel>> featureValues)
246   {
247     // default does nothing, override where this is implemented
248     return null;
249   }
250
251   @Override
252   public List<StructureCommandI> startNotifications(String uri)
253   {
254     return null;
255   }
256
257   @Override
258   public List<StructureCommandI> stopNotifications()
259   {
260     return null;
261   }
262
263   @Override
264   public StructureCommandI getSelectedResidues()
265   {
266     return null;
267   }
268
269   @Override
270   public StructureCommandI listResidueAttributes()
271   {
272     return null;
273   }
274
275   @Override
276   public StructureCommandI getResidueAttributes(String attName)
277   {
278     return null;
279   }
280   
281   @Override
282   public StructureCommandI restoreSession(String filePath)
283   {
284     return loadFile(filePath);
285   }
286 }