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