JAL-3551 working proof of concept of Jalview driving PyMOL
[jalview.git] / src / jalview / structure / StructureCommandsBase.java
1 package jalview.structure;
2
3 import jalview.api.AlignmentViewPanel;
4 import jalview.datamodel.SequenceI;
5
6 import java.awt.Color;
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Map.Entry;
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    * 
91    * @param colourMap
92    * @return
93    */
94   @Override
95   public List<StructureCommandI> colourBySequence(
96           Map<Object, AtomSpecModel> colourMap)
97   {
98     /*
99      * default implementation creates one command per colour;
100      * override to concatenate colour commands if wanted
101      */
102     List<StructureCommandI> commands = new ArrayList<>();
103     for (Object key : colourMap.keySet())
104     {
105       Color colour = (Color) key;
106       final AtomSpecModel colourData = colourMap.get(colour);
107       commands.add(getColourCommand(colourData, colour));
108     }
109
110     return commands;
111   }
112
113   /**
114    * Returns a command to colour the atoms represented by {@code atomSpecModel}
115    * with the colour specified by {@code colourCode}.
116    * 
117    * @param atomSpecModel
118    * @param colour
119    * @return
120    */
121   protected StructureCommandI getColourCommand(AtomSpecModel atomSpecModel,
122           Color colour)
123   {
124     String atomSpec = getAtomSpec(atomSpecModel, false);
125     return getColourCommand(atomSpec, colour);
126   }
127
128   /**
129    * Returns a command to colour the atoms described (in viewer command syntax)
130    * by {@code atomSpec} with the colour specified by {@code colourCode}
131    * 
132    * @param atomSpec
133    * @param colour
134    * @return
135    */
136   protected abstract StructureCommandI getColourCommand(String atomSpec,
137           Color colour);
138
139   @Override
140   public List<StructureCommandI> colourByResidues(
141           Map<String, Color> colours)
142   {
143     List<StructureCommandI> commands = new ArrayList<>();
144     for (Entry<String, Color> entry : colours.entrySet())
145     {
146       commands.add(colourResidue(entry.getKey(), entry.getValue()));
147     }
148     return commands;
149   }
150
151   private StructureCommandI colourResidue(String resName, Color col)
152   {
153     String atomSpec = getResidueSpec(resName);
154     return getColourCommand(atomSpec, col);
155   }
156
157   /**
158    * Helper method to append one start-end range to an atomspec string
159    * 
160    * @param sb
161    * @param start
162    * @param end
163    * @param chain
164    * @param firstPositionForModel
165    */
166   protected void appendRange(StringBuilder sb, int start, int end,
167           String chain, boolean firstPositionForModel, boolean isChimeraX)
168   {
169     if (!firstPositionForModel)
170     {
171       sb.append(",");
172     }
173     if (end == start)
174     {
175       sb.append(start);
176     }
177     else
178     {
179       sb.append(start).append("-").append(end);
180     }
181
182     if (!isChimeraX)
183     {
184       sb.append(".");
185       if (!" ".equals(chain))
186       {
187         sb.append(chain);
188       }
189     }
190   }
191
192   /**
193    * Returns the atom specifier meaning all occurrences of the given residue
194    * 
195    * @param residue
196    * @return
197    */
198   protected abstract String getResidueSpec(String residue);
199 }