001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojava.nbio.structure.xtal; 022 023import org.biojava.nbio.structure.xtal.io.SpaceGroupMapRoot; 024import org.slf4j.Logger; 025import org.slf4j.LoggerFactory; 026 027import javax.xml.bind.JAXBException; 028import java.io.BufferedReader; 029import java.io.IOException; 030import java.io.InputStream; 031import java.io.InputStreamReader; 032import java.util.HashMap; 033import java.util.TreeMap; 034import java.util.regex.Matcher; 035import java.util.regex.Pattern; 036 037 038/** 039 * A class containing static methods to parse the symop.lib file from the 040 * CCP4 package. The file contains the transformations belonging to all 041 * protein crystallography space groups. 042 * 043 * See http://structure.usc.edu/ccp4/symlib.html for documentation 044 * 045 * @author duarte_j 046 * 047 */ 048public class SymoplibParser { 049 050 private static final Logger logger = LoggerFactory.getLogger(SymoplibParser.class); 051 052 private static final String newline = System.getProperty("line.separator"); 053 054 private static final String SPACE_GROUPS_FILE = "org/biojava/nbio/structure/xtal/spacegroups.xml"; 055 056 private static final Pattern namePat = Pattern.compile(".*\\s([A-Z]+)(\\s'.+')?\\s+'(.+)'.*"); 057 058 private static TreeMap<Integer, SpaceGroup> sgs = parseSpaceGroupsXML(); 059 060 061 private static HashMap<String, SpaceGroup> name2sgs; // map for lookups based on short names 062 063 /** 064 * Gets the space group for the given standard identifier. 065 * See for example http://en.wikipedia.org/wiki/Space_group 066 * @param id 067 * @return 068 */ 069 public static SpaceGroup getSpaceGroup(int id) { 070 return sgs.get(id); 071 } 072 073 074 /** 075 * Load all SpaceGroup information from the file spacegroups.xml 076 * 077 * @return a map providing information for all spacegroups 078 */ 079 private static TreeMap<Integer, SpaceGroup> parseSpaceGroupsXML() { 080 081 // NOTE: if the space group file is requested by some part of the code (i.e. this method is called) and 082 // there is a problem in reading it, then that's truly a FATAL problem, since this is not a user file 083 // but a file that's part of the distribution: it MUST be there and MUST have the right format. A failure 084 // to read it is more of a "compilation" error than a runtime error. That's the reason that System.exit 085 // is called (which otherwise usually is not a good idea). 086 // 087 // The rest of the application will simply not work: there are 3 options to handle it 088 // a) returning null and then a NullPointer will happen down the line and thus a not very clear 089 // error message will be printed 090 // b) throw the exception forward and catch it in the final main but that would also be bad because 091 // this is a file that the user didn't input but that should be part of the distribution 092 // c) call System.exit(1) and "crash" the application with a human-understandable error message 093 094 InputStream spaceGroupIS = SymoplibParser.class.getClassLoader().getResourceAsStream(SPACE_GROUPS_FILE); 095 096 if ( spaceGroupIS == null) { 097 logger.error("Fatal error! Could not find resource: " + SPACE_GROUPS_FILE + ". This probably means that your biojava jar file is corrupt or incorrectly built."); 098 System.exit(1); 099 } 100 101 TreeMap<Integer, SpaceGroup> map = new TreeMap<Integer, SpaceGroup>(); 102 103 try { 104 map = parseSpaceGroupsXML(spaceGroupIS); 105 } catch (IOException e) { 106 logger.error("Fatal error! Could not parse resource: "+SPACE_GROUPS_FILE+". Error: "+e.getMessage()); 107 System.exit(1); 108 } catch (JAXBException e) { 109 logger.error("Fatal error! Could not parse resource: "+SPACE_GROUPS_FILE+". Problem in xml formatting: "+e.getMessage()); 110 System.exit(1); 111 } 112 113 name2sgs = new HashMap<String, SpaceGroup>(); 114 115 for (SpaceGroup sg:map.values()) { 116 117 sg.initializeCellTranslations(); 118 name2sgs.put(sg.getShortSymbol(), sg); 119 if (sg.getAltShortSymbol()!=null) { 120 // we add also alternative name to map so we can look it up 121 name2sgs.put(sg.getAltShortSymbol(), sg); 122 } 123 } 124 125 return map; 126 127 } 128 129 130 /** 131 * Load all SpaceGroup information from the file spacegroups.xml 132 * 133 * @return a map providing information for all spacegroups 134 */ 135 public static TreeMap<Integer, SpaceGroup> parseSpaceGroupsXML( 136 InputStream spaceGroupIS) throws IOException, JAXBException { 137 138 String xml = convertStreamToString(spaceGroupIS); 139 140 SpaceGroupMapRoot spaceGroups = SpaceGroupMapRoot.fromXML(xml); 141 return spaceGroups.getMapProperty(); 142 143 } 144 145 146 private static String convertStreamToString(InputStream stream) throws IOException { 147 BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); 148 StringBuilder sb = new StringBuilder(); 149 150 String line = null; 151 152 while ((line = reader.readLine()) != null) { 153 sb.append(line).append(newline); 154 } 155 156 return sb.toString(); 157 } 158 159 /** 160 * Get the space group for the given international short name, using 161 * the PDB format, e.g. 'P 21 21 21' or 'C 1 c 1' 162 * @param shortName 163 * @return the SpaceGroup or null if the shortName is not valid 164 */ 165 public static SpaceGroup getSpaceGroup(String shortName) { 166 if (shortName==null || shortName.length()<=2) return null; 167 168 // PDB uses group "P 1-" for 13 racemic mixture entries (as of Sep2011), e.g. 3e7r 169 // they call the space group "P 1-" unusually (symop.lib and everyone else call it "P -1") 170 if (shortName.equals("P 1-")) shortName="P -1"; 171 172 // enantiomorphic space groups contain sometime letters indicating glide planes which should always be lower case 173 // in some PDB entries like 4gwv they are in upper case, we fix that here: convert any non-first letter to lower case 174 shortName = shortName.substring(0, 1)+shortName.substring(1).toLowerCase(); 175 176 return name2sgs.get(shortName); 177 } 178 179 public static TreeMap<Integer,SpaceGroup> getAllSpaceGroups() { 180 return sgs; 181 } 182 183 184 /** 185 * A parser for the symop.lib file provided by CCP4. Note: this file is not getting re-distributed by BioJava. 186 * It can be downloaded from: 187 * 188 * http://www.ccp4.ac.uk/cvs/viewvc.cgi/libccp4/data/symop.lib?revision=1.10&view=markup 189 * 190 * Note: this file is not needed by BioJava. BioJava loads equivalent information from the file spacegroups.xml 191 * 192 * @param symoplibIS 193 * @return 194 */ 195 public static TreeMap<Integer,SpaceGroup> parseSymopLib(InputStream symoplibIS) { 196 TreeMap<Integer, SpaceGroup> map = new TreeMap<Integer, SpaceGroup>(); 197 name2sgs = new HashMap<String, SpaceGroup>(); 198 try { 199 BufferedReader br = new BufferedReader(new InputStreamReader(symoplibIS)); 200 String line; 201 SpaceGroup currentSG = null; 202 while ((line=br.readLine())!=null) { 203 if (!line.startsWith(" ")) { 204 if (currentSG!=null) { 205 map.put(currentSG.getId(),currentSG); 206 name2sgs.put(currentSG.getShortSymbol(), currentSG); 207 if (currentSG.getAltShortSymbol()!=null) { 208 // we add also alternative name to map so we can look it up 209 name2sgs.put(currentSG.getAltShortSymbol(), currentSG); 210 } 211 } 212 213 int idxFirstSpace = line.indexOf(' '); 214 int idxSecondSpace = line.indexOf(' ',idxFirstSpace+1); 215 int idxThirdSpace = line.indexOf(' ',idxSecondSpace+1); 216 int id = Integer.parseInt(line.substring(0, idxFirstSpace)); 217 int multiplicity = Integer.parseInt(line.substring(idxFirstSpace+1, idxSecondSpace)); 218 int primitiveMultiplicity = Integer.parseInt(line.substring(idxSecondSpace+1, idxThirdSpace)); 219 Matcher m = namePat.matcher(line); 220 String shortSymbol = null; 221 String altShortSymbol = null; 222 String brav = null; 223 if (m.matches()) { 224 brav = m.group(1); 225 altShortSymbol = m.group(2); // null if there is no match 226 if (altShortSymbol!=null) altShortSymbol = altShortSymbol.trim().replaceAll("'", ""); 227 shortSymbol = m.group(3); 228 } 229 currentSG = new SpaceGroup(id, multiplicity, primitiveMultiplicity, shortSymbol, altShortSymbol, BravaisLattice.getByName(brav)); 230 } else { 231 currentSG.addTransformation(line.trim()); 232 } 233 } 234 br.close(); 235 // and we add the last SG 236 map.put(currentSG.getId(), currentSG); 237 name2sgs.put(currentSG.getShortSymbol(), currentSG); 238 if (currentSG.getAltShortSymbol()!=null) { 239 // we add also alternative name to map so we can look it up 240 name2sgs.put(currentSG.getAltShortSymbol(), currentSG); 241 } 242 243 } catch (IOException e) { 244 logger.error("Fatal error! Can't read symop.lib file. Error: "+e.getMessage()+". "); 245 System.exit(1); 246 } 247 248 for (SpaceGroup sg:map.values()) { 249 sg.initializeCellTranslations(); 250 } 251 return map; 252 } 253 254 255}