View Javadoc

1   package uk.ac.ebi.intenz.tools.sib.writer;
2   
3   import java.io.File;
4   import java.io.FileWriter;
5   import java.io.IOException;
6   import java.io.InputStream;
7   import java.io.StringWriter;
8   import java.io.Writer;
9   import java.text.SimpleDateFormat;
10  import java.util.ArrayList;
11  import java.util.Arrays;
12  import java.util.Collection;
13  import java.util.Collections;
14  import java.util.Date;
15  import java.util.HashMap;
16  import java.util.Iterator;
17  import java.util.List;
18  import java.util.Map;
19  import java.util.Properties;
20  import org.apache.log4j.Logger;
21  
22  import uk.ac.ebi.biobabel.validator.DbIdentifierValidator;
23  import uk.ac.ebi.intenz.tools.sib.sptr_enzyme.EnzymeCrossReference;
24  import uk.ac.ebi.intenz.tools.sib.sptr_enzyme.EnzymeEntryImpl;
25  import uk.ac.ebi.interfaces.sptr.SPTRCrossReference;
26  import uk.ac.ebi.interfaces.sptr.SPTRException;
27  
28  /**
29   * This utility class provides methods for exporting enzyme data into the ENZYME flat file format.
30   * <br/><br/>
31   * <p/>
32   * It adds the standard <code>enzyme.dat</code> header containing the current date
33   * (<code>d-MMM-yyyy</code> format). This header looks as follows:
34   * <p/>
35   * <p/>
36   * <code>
37   * <b>
38   * CC   -----------------------------------------------------------------------<br/>
39   * CC<br/>
40   * CC   ENZYME nomenclature database<br/>
41   * CC<br/>
42   * CC   -----------------------------------------------------------------------<br/>
43   * CC   Release 21-Mar-2007<br/>
44   * CC<br/>
45   * CC   Amos Bairoch and Kristian Axelsen<br/>
46   * CC   Swiss Institute of Bioinformatics (SIB)<br/>
47   * CC   Centre Medical Universitaire (CMU)<br/>
48   * CC   1, rue Michel Servet<br/>
49   * CC   1211 Geneva 4<br/>
50   * CC   Switzerland<br/>
51   * CC<br/>
52   * CC   Email: enzyme@isb-sib.ch<br/>
53   * CC   Telephone: +41-22-379 50 50<br/>
54   * CC   Fax: +41-22-379 58 58<br/>
55   * CC<br/>
56   * CC   WWW server: http://enzyme.expasy.org/<br/>
57   * CC<br/>
58   * CC   -----------------------------------------------------------------------<br/>
59   * CC   This database is copyright from the Swiss Institute of Bioinformatics.<br/>
60   * CC   There are  no restrictions  on  its use by any institutions as long as<br/>
61   * CC   its content is in no way modified.<br/>
62   * CC   -----------------------------------------------------------------------<br/>
63   * //
64   * </b>
65   * </code>
66   * <br/><br/>
67   * <p/>
68   * An example of an ENZYME entry is shown below:
69   * <p/>
70   * <p/>
71   * <code>
72   * <b>
73   * ID   1.1.1.2<br/>
74   * DE   Alcohol dehydrogenase (NADP+).<br/>
75   * AN   Aldehyde reductase (NADPH).<br/>
76   * CA   An alcohol + NADP(+) = an aldehyde + NADPH.<br/>
77   * CF   Zinc.<br/>
78   * CC   -!- Some members of this group oxidize only primary alcohols; others act<br/>
79   * CC       also on secondary alcohols.<br/>
80   * CC   -!- May be identical with EC 1.1.1.19, EC 1.1.1.33 and EC 1.1.1.55.<br/>
81   * CC   -!- A-specific with respect to NADPH.<br/>
82   * PR   PROSITE; PDOC00061;<br/>
83   * DR   P35630, ADH1_ENTHI;  Q24857, ADH3_ENTHI;  O57380, ADH4_RANPE;<br/>
84   * DR   P25984, ADH_CLOBE ;  P75214, ADH_MYCPN ;  P31975, ADH_MYCTU ;<br/>
85   * DR   P14941, ADH_THEBR ;  O70473, AKA1_CRIGR;  P14550, AKA1_HUMAN;<br/>
86   * DR   Q9JII6, AKA1_MOUSE;  P50578, AKA1_PIG  ;  P51635, AKA1_RAT  ;<br/>
87   * DR   Q9UUN9, ALD2_SPOSA;  P27800, ALDX_SPOSA;<br/>
88   * //<br/>
89   * </b>
90   * </code>
91   * <br/><br/>
92   * The creation of this file follows the rules defined
93   * <a href="http://enzyme.expasy.org/enzuser.txt">here</a>.
94   *
95   * @author Michael Darsow
96   * @version $Revision: 1.3 $ $Date: 2008/04/01 14:26:09 $
97   */
98  public class EnzymeFlatFileWriter {
99  
100   private static final Logger LOGGER =
101 	  Logger.getLogger(EnzymeFlatFileWriter.class.getName());
102   
103   /** EC numbers of entries which have been transferred more than once,
104    * or to more than one entry, or deleted but actually transferred,
105    * and whose DE line must be changed to match the enzyme.dat entry. */
106 //  public static final List MULTI_TRANSFER_ENTRIES = new ArrayList();
107   public static final Properties DE_MODIFIED_ENTRIES = new Properties();
108 
109   static {
110 	  InputStream stream;
111 	  stream = EnzymeFlatFileWriter.class.getClassLoader().getResourceAsStream("DE_line_modifications.properties");
112 	  try {
113 		  DE_MODIFIED_ENTRIES.load(stream);
114 	  } catch (IOException e) {
115 		  LOGGER.error(e.getMessage(), e);
116 	  } finally {
117 		  try {
118 			  if (stream != null) stream.close();
119 		  } catch (IOException e){}
120 	  }
121   }
122 
123   /**
124    * Used for lazy initialisation of the flat file header.
125    */
126   private static String header = "";
127 
128   /**
129    * This class should never be instantiated nor subclassed.
130    */
131   private EnzymeFlatFileWriter() {
132   }
133 
134    /**
135     * Gives single enzyme view (used in webapp tools).
136     * @param enzyme
137     * @return
138     * @throws SPTRException
139     */
140   public static String export(EnzymeEntryImpl enzyme) throws SPTRException {
141     if (enzyme == null) throw new NullPointerException("Parameter 'enzyme' must not be null.");
142     StringWriter sw = new StringWriter();
143     try {
144         export(enzyme, sw);
145     } catch (IOException e) {
146         throw new EnzymeFlatFileWriteException(e);
147     }
148     return sw.toString();
149   }
150 
151   /**
152    * Exports the given collection of enzymes into the ENZYME flat file format using the given file.
153    *
154    * @param enzymes    The collection of {@link EnzymeEntry EnzymeEntry} instances.
155    * @param version    Release version.
156    * @param outputFile The file storing the enzyme information.
157    * @return the time elapsed during this process.
158    * @throws NullPointerException     if any of the given parameters is <code>null</code>
159    * @throws IllegalArgumentException if <code>version</code> does not match the following regular expression pattern:
160    *                                  <code>\d+\.\d+</code>.
161    * @throws SPTRException            if an error occured during the export process.
162    */
163   public static long export(Collection enzymes, String version, File outputFile) throws SPTRException {
164     if (enzymes == null) throw new NullPointerException("Parameter enzymes == null not allowed here.");
165     if (version == null) throw new NullPointerException("Parameter version == null not allowed here.");
166 //    if (!version.matches("\\d+?\\.\\d+?"))   todo: do something about version number
167 //     if(!version.matches("\\d+?"))
168 //      throw new IllegalArgumentException("Parameter version must match the following regular expression: \\d+\\.\\d+");
169     if (outputFile == null) throw new NullPointerException("Parameter outputFile == null not allowed here.");
170 
171     FileWriter fileWriter = null;
172     long start = System.currentTimeMillis();
173 
174     try {
175       fileWriter = new FileWriter(outputFile);
176       fileWriter.write(getHeader(version));
177 
178       Iterator enzymeIterator = enzymes.iterator();
179 
180       while (enzymeIterator.hasNext()) {
181         EnzymeEntryImpl enzymeEntry = (EnzymeEntryImpl) enzymeIterator.next();
182         export(enzymeEntry, fileWriter);
183       }
184     } catch (UnsupportedOperationException e) {
185       throw new EnzymeFlatFileWriteException(e);
186     } catch (IOException e) {
187       throw new EnzymeFlatFileWriteException(e);
188     } finally {
189       try {
190         if (fileWriter != null) fileWriter.close();
191       } catch (IOException e) {
192         throw new EnzymeFlatFileWriteException(e);
193       }
194     }
195 
196     return System.currentTimeMillis() - start;
197   }
198 
199     /**
200      * @param enzymeEntry
201      * @param writer
202      * @throws SPTRException
203      * @throws IOException
204      */
205     public static void export(EnzymeEntryImpl enzymeEntry, Writer writer)
206         throws SPTRException, IOException {
207         Map enzymeCrossReferences = getGroupedEnzymeCrossReferences(enzymeEntry.getCrossReferences());        
208         // 0 - EC (ID)
209         writer.write(createIDLine(enzymeEntry.getEC()));
210         if (!enzymeEntry.isTransferredOrDeleted()) {
211             // 1 - common name (DE)
212           writer.write(createDELine(enzymeEntry.getCommonName(), true));
213           // 2 - other names (AN)
214           writer.write(createANLines(enzymeEntry.getSynonyms(), true));
215           // 3 - reaction(s) (CA)
216           writer.write(createCALines(enzymeEntry.getReactions(), true));
217           // 4 - cofactors (CF)
218           writer.write(createCFLines(enzymeEntry.getCofactors(), true));
219           // 5 - comments (CC)
220           writer.write(createCCLines(enzymeEntry.getComment(), true));
221           // 6 - MIM links (DI)
222           writer.write(createDILines((ArrayList) enzymeCrossReferences.get(EnzymeCrossReference.MIM)));
223           // 7 - PROSITE links (PR)
224           writer.write(createPRLines((ArrayList) enzymeCrossReferences.get(EnzymeCrossReference.PROSITE)));
225           // 8 - SWISSPROT links (DR)
226           writer.write(createDRLines((ArrayList) enzymeCrossReferences.get(EnzymeCrossReference.SWISSPROT)));
227         } else {
228             // Fix for transferred entries:
229 //            String deLine = MULTI_TRANSFER_ENTRIES.contains(enzymeEntry.getEC()) ?
230 //                    XCharsASCIITranslator.getInstance().toASCII(enzymeEntry.getCommonName(), false, true) :
231 //                    enzymeEntry.getCommonName();
232             String deLine = DE_MODIFIED_ENTRIES.containsKey(enzymeEntry.getEC())?
233         		DE_MODIFIED_ENTRIES.getProperty(enzymeEntry.getEC()):
234         		enzymeEntry.getCommonName();
235             writer.write(createDELine(deLine, true));
236         }
237         writer.write("//\n");
238         writer.flush();
239     }
240 
241   /**
242    * Exports the given collection of enzymes into the ENZYME flat file format using the given directory and file name
243    * to create the file.
244    *
245    * @param enzymes  The collection of {@link EnzymeEntry EnzymeEntry} instances.
246    * @param version  Release version.
247    * @param dir      A valid directory where the file should be stored. If this value is either <code>null</code> or empty the file
248    *                 will be stored in the current working directory.
249    * @param fileName Name of the file which stores the enzyme information. If this value is either <code>null</code> or
250    *                 empty then the file will be named <code>enzyme.dat</code>.
251    * @return the time elapsed during this process.
252    * @throws NullPointerException     if <code>enzymes</code> or <code>version</code> is <code>null</code>.
253    * @throws IllegalArgumentException if <code>version</code> does not match the following regular expression pattern:
254    *                                  <code>\d+\.\d+</code> and if <code>dir</code> is not an existing directory.
255    * @throws SPTRException            if an error occured during the export process.
256    */
257   public static long export(Collection enzymes, String version, String dir, String fileName) throws SPTRException {
258     if (enzymes == null) throw new NullPointerException("Parameter enzymes == null not allowed here.");
259     if (version == null) throw new NullPointerException("Parameter version == null not allowed here.");
260 //    if (!version.matches("\\d+\\.\\d+"))    todo: fix this
261 //      throw new IllegalArgumentException("Parameter version must match the following regular expression: \\d+\\.\\d+");
262 
263     // Check file parameters and create file instance.
264     StringBuilder pathname = new StringBuilder();
265     if (dir != null && !dir.equals("")) {
266       File dirTest = new File(dir);
267       if (!dirTest.isDirectory()) throw new IllegalArgumentException("Parameter dir is not a valid pathname");
268       pathname.append(dir);
269       if (!dir.endsWith("/") && !dir.endsWith("\\")) pathname.append(File.separator);
270     }
271     if (fileName == null || fileName.equals(""))
272       pathname.append("enzyme.dat");
273     else
274       pathname.append(fileName);
275     File outputFile = new File(pathname.toString());
276 
277     return export(enzymes, version, outputFile);
278   }
279 
280 
281   // ------------- PRIVATE METHODS ----------------------------
282 
283    /**
284     * Sort lines in alphabetical order where necessary
285     * @param synonyms
286     * @return
287     * @throws SPTRException
288     */
289    private static String[] sortLines (String[] synonyms) throws SPTRException {
290       List synonymListToSort = Arrays.asList(synonyms);
291       Collections.sort(synonymListToSort);
292       return (String[]) synonymListToSort.toArray();
293    }
294 
295   /**
296    * Creates the ID line.
297    * <p/>
298    * If <code>ec == null</code> or <code>ec.equals("")</code> an Exception will be thrown because the ID line is a
299    * mandatory line tytpe.
300    *
301    * @param ec The ID line only contains the EC number.
302    * @return the ID line.
303    * @throws SPTRException if the <code>ec</code> does not contain a valid EC.
304    */
305   private static String createIDLine(String ec) throws SPTRException {
306     assert ec != null : ec;
307     if (!DbIdentifierValidator.getInstance().validate(ec, DbIdentifierValidator.EC_NUMBER))
308     	throw new EnzymeFlatFileWriteException("Parameter ec does not contain a valid ec.");
309 
310     LOGGER.debug("EC: " + ec);
311 
312     StringBuilder IDContent = new StringBuilder();
313     IDContent.append("ID   ");
314     IDContent.append(ec);
315     IDContent.append("\n");
316 
317     return IDContent.toString();
318   }
319 
320   /**
321    * Creates the DE line.
322    * <p/>
323    * If <code>commonName == null</code> or <code>commonName.equals("")</code> an Exception will be thrown because
324    * the DE line is a mandatory line type.
325    *
326    * @param commonName The common name usually fills the DE line but could also show history information in terms or
327    *                   transferred or deleted entries.
328    * @param wrapLines
329    * @return the DE line.
330    * @throws SPTRException if no output could be created for this DE line.
331    */
332   private static String createDELine(String commonName, boolean wrapLines) throws SPTRException {
333     assert commonName != null : commonName;
334 
335 //    if (commonName.equals("")) throw new EnzymeFlatFileWriteException("The DE line must not contain an empty string.");
336 
337     StringBuilder DEContent = new StringBuilder();
338     if (wrapLines) commonName = insertLineBreaks(commonName, LineType.DE);
339     DEContent.append(commonName);
340     DEContent.append("\n");
341 
342     return DEContent.toString();
343   }
344 
345   /**
346    * Creates the AN line by using one line per alternative name.
347    * <p/>
348    * At the moment the AN lines do not follow a particular order.
349    *
350    * @param synonyms  All synonyms of this enzyme arranged in an array.
351    * @param wrapLines
352    * @return the AN line(s) or an empty string if <code>synonyms</code> was either <code>null</code> or empty.
353    * @throws SPTRException if no output could be created for an AN line.
354    */
355   private static String createANLines(String[] synonyms, boolean wrapLines) throws SPTRException {
356     assert synonyms != null : synonyms;
357 
358     StringBuilder ANContent = new StringBuilder();
359     for (int iii = 0; iii < synonyms.length; iii++) {
360       String synonym = synonyms[iii];
361       if (synonym.equals("") || synonym.equals("-")) continue;
362       if (wrapLines) synonym = insertLineBreaks(synonyms[iii], LineType.AN);
363       ANContent.append(synonym);
364       ANContent.append("\n");
365     }
366 
367     return ANContent.toString();
368   }
369 
370   /**
371    * Creates the CA line by using one line per reaction.
372    *
373    * @param reactions All reactions of this enzyme arranged in an array.
374    * @param wrapLines
375    * @return the CA line(s) or an empty string if <code>reactions</code> was either <code>null</code> or empty.
376    * @throws SPTRException if no output could be created for an CA line.
377    */
378   private static String createCALines(String[] reactions, boolean wrapLines) throws SPTRException {
379     assert reactions != null : reactions;
380 
381     StringBuilder CAContent = new StringBuilder();
382     for (int iii = 0; iii < reactions.length; iii++) {
383       if (reactions[iii].equals("")) continue;
384       // Removing period at the end of a descriptive reaction.
385       StringBuffer reaction = null;
386       if (reactions[iii].charAt(reactions[iii].length() - 1) == '.')
387         reaction = new StringBuffer(reactions[iii].substring(0, reactions[iii].length() - 1));
388       else
389         reaction = new StringBuffer(reactions[iii]);
390       if (reactions.length > 1) {
391         StringBuilder number = new StringBuilder("(");
392         number.append(iii + 1);
393         number.append(") ");
394         reaction.insert(0, number.toString());
395       }
396       if (wrapLines)
397         CAContent.append(insertLineBreaks(reaction.toString(), LineType.CA));
398       else
399         CAContent.append(reaction.toString());
400       CAContent.append("\n");
401     }
402 
403     return CAContent.toString();
404   }
405 
406   /**
407    * Creates the CF line.
408    *
409    * @param cofactors The cofactor string of this enzyme.
410    * @param wrapLines
411    * @return the CF line or an empty string if <code>cofactors</code> was either <code>null</code> or empty.
412    * @throws SPTRException if no output could be created for an CF line.
413    */
414   private static String createCFLines(String cofactors, boolean wrapLines) throws SPTRException {
415     assert cofactors != null : cofactors;
416 
417     if (cofactors.equals("")) return cofactors;
418 
419     StringBuilder CFContent = new StringBuilder();
420     if (wrapLines) cofactors = insertLineBreaks(cofactors, LineType.CF);
421     CFContent.append(cofactors);
422     CFContent.append("\n");
423 
424     return CFContent.toString();
425   }
426 
427   /**
428    * Creates the CC line(s).
429    * <p/>
430    * Each sentence starts with <code>CC   -!-</code> and ends with a period.
431    *
432    * @param comment   The comment of this enzyme.
433    * @param wrapLines
434    * @return The CC line(s) or an empty string if <code>comment</code> was either <code>null</code> or empty.
435    * @throws SPTRException if no output could be created for an CC line.
436    */
437   private static String createCCLines(String comment, boolean wrapLines) throws SPTRException {
438     assert comment != null : comment;
439     if (comment.equals("")) return comment;
440     if (wrapLines) comment = insertLineBreaks(comment, LineType.CC);
441     return comment;
442   }
443 
444   /**
445    * Creates the DI line by simply creating one line per MIM cross reference.
446    *
447    * @param DICrossReferences All ENZYME cross references of this enzyme to the MIM database.
448    * @return the DI line(s) or an empty string if <code>DICrossReferences</code> was either <code>null</code> or empty.
449    */
450   private static String createDILines(ArrayList DICrossReferences) {
451     assert DICrossReferences != null : DICrossReferences;
452 
453     StringBuilder DIContent = new StringBuilder();
454     for (int iii = 0; iii < DICrossReferences.size(); iii++) {
455       EnzymeCrossReference enzymeCrossReference = (EnzymeCrossReference) DICrossReferences.get(iii);
456       DIContent.append("DI   ");
457       DIContent.append(enzymeCrossReference.getPropertyValue(EnzymeCrossReference.PROPERTY_DESCRIPTION));
458       DIContent.append("; MIM:");
459       DIContent.append(enzymeCrossReference.getAccessionNumber());
460       DIContent.append(".\n");
461     }
462 
463     return DIContent.toString();
464   }
465 
466   /**
467    * Creates the PR line by simply creating one line per PROSITE cross reference.
468    *
469    * @param PRCrossReferences All ENZYME cross references of this enzyme to the PROSITE database.
470    * @return the PR line(s) or an empty string if <code>PRCrossReferences</code> was either <code>null</code> or empty.
471    */
472   private static String createPRLines(ArrayList PRCrossReferences) {
473     assert PRCrossReferences != null : PRCrossReferences;
474 
475     StringBuilder PRContent = new StringBuilder();
476     for (int iii = 0; iii < PRCrossReferences.size(); iii++) {
477       EnzymeCrossReference enzymeCrossReference = (EnzymeCrossReference) PRCrossReferences.get(iii);
478       PRContent.append("PR   ");
479       PRContent.append(enzymeCrossReference.getDatabaseName());
480       PRContent.append("; ");
481       PRContent.append(enzymeCrossReference.getAccessionNumber());
482       PRContent.append(";\n");
483     }
484 
485     return PRContent.toString();
486   }
487 
488   /**
489    * Loads the information needed for the DR line(s).
490    * <p/>
491    * This method assumes that the DR elements are sorted by name.
492    *
493    * @param DRCrossReferences All ENZYME cross references of this enzyme to the SwissProt database.
494    * @return the DR line(s) or an empty string if <code>DRCrossReferences</code> was either <code>null</code> or empty.
495    */
496   private static String createDRLines(ArrayList DRCrossReferences) {
497     assert DRCrossReferences != null : DRCrossReferences;
498 
499     StringBuilder DRContent = new StringBuilder();
500     int count = 0;
501     for (int iii = 0; iii < DRCrossReferences.size(); iii++) {
502       EnzymeCrossReference enzymeCrossReference = (EnzymeCrossReference) DRCrossReferences.get(iii);
503       if (count == 0){
504         DRContent.append("DR   ");
505       }
506       // UniProt accessions may have from 6 up to 10 characters:
507       DRContent.append(getPaddedString(
508               enzymeCrossReference.getAccessionNumber(), 10));
509       DRContent.append(", ");
510       DRContent.append(getPaddedString(enzymeCrossReference.getPropertyValue(
511               EnzymeCrossReference.PROPERTY_DESCRIPTION), 11));
512       DRContent.append(";  ");
513       if (count == 2) {
514         count = 0;
515         DRContent.delete(DRContent.length() - 2, DRContent.length()); // Remove spaces after last semicolon.
516         DRContent.append("\n");
517         continue;
518       }
519 
520       count++;
521     }
522 
523     if (DRContent.length() > 1) {
524       if (DRContent.charAt(DRContent.length() - 1) == ' ')
525         DRContent.delete(DRContent.length() - 2, DRContent.length()); // Remove spaces after last semicolon.
526       if (count > 0 && count < 3)
527         DRContent.append("\n");
528     }
529 
530     return DRContent.toString();
531   }
532 
533   /**
534    * Groups all <code>SPTRCrossReferences</code> into the three groups which occur in the <code>enzyme.dat</code> file.
535    * <p/>
536    * These groups are MIM, PROSITE and SwissProt xrefs.
537    *
538    * @param allCrossReferences
539    * @return a <code>Hashtable</code> containing the thre groups. The keys are the
540    *         {@link EnzymeCrossReference EnzymeCrossReference} constants.
541    */
542   private static Map getGroupedEnzymeCrossReferences(SPTRCrossReference[] allCrossReferences) {
543     Map groupedCrossReferences = new HashMap();
544 
545     ArrayList DICrossReferences = new ArrayList();
546     ArrayList PRCrossReferences = new ArrayList();
547     ArrayList DRCrossReferences = new ArrayList();
548 
549     for (int iii = 0; iii < allCrossReferences.length; iii++) {
550       EnzymeCrossReference crossReference = (EnzymeCrossReference) allCrossReferences[iii];
551       if (crossReference.getDatabaseName().equals(EnzymeCrossReference.MIM)) {
552         DICrossReferences.add(crossReference);
553       }
554       if (crossReference.getDatabaseName().equals(EnzymeCrossReference.PROSITE)) {
555         PRCrossReferences.add(crossReference);
556       }
557       if (crossReference.getDatabaseName().equals(EnzymeCrossReference.SWISSPROT)) {
558         DRCrossReferences.add(crossReference);
559       }
560     }
561 
562     groupedCrossReferences.put(EnzymeCrossReference.MIM, DICrossReferences);
563     groupedCrossReferences.put(EnzymeCrossReference.PROSITE, PRCrossReferences);
564     groupedCrossReferences.put(EnzymeCrossReference.SWISSPROT, DRCrossReferences);
565 
566     return groupedCrossReferences;
567   }
568   
569   /**
570    * Pads a string with trailing spaces up to a given length.
571    * @param s the string to pad.
572    * @param length the desired final length.
573    * @return a string with trailing spaces of length <code>length</code>.
574    */
575   private static String getPaddedString(String s, int length){
576       if (s.length() == length) return s;
577       StringBuilder sb = new StringBuilder(s);
578       for (int i = s.length(); i < length; i++){
579           sb.append(' ');
580       }
581       return sb.toString();
582   }
583 
584   /**
585    * Lazy initialisation of the class member <code>header</code> which represents the flat file header.
586    * <p/>
587    * It uses the given version number and adds the current data (month and year) automatically.
588    *
589    * @return the flat file header.
590    */
591   private static String getHeader(String version) {
592     assert version != null : version;
593 
594     if (header.equals("")) {
595       Date today = new Date();
596       SimpleDateFormat formatter = new SimpleDateFormat("d-MMM-yyyy");
597 
598       StringBuffer enzymeHeader = new StringBuffer()
599       		.append("CC   -----------------------------------------------------------------------\n")
600 			.append("CC\n")
601 			.append("CC   ENZYME nomenclature database\n")
602 			.append("CC\n")
603 			.append("CC   -----------------------------------------------------------------------\n")
604 			.append("CC   Release of ")
605 			.append(formatter.format(today))
606 			.append("\n")
607 			.append("CC   -----------------------------------------------------------------------\n")
608 			.append("CC\n")
609 			.append("CC   Ioannis Xenarios and Kristian Axelsen\n")
610 			.append("CC   SIB Swiss Institute of Bioinformatics\n")
611 			.append("CC   Centre Medical Universitaire (CMU)\n")
612 			.append("CC   1, rue Michel Servet\n")
613 			.append("CC   1211 Geneva 4\n")
614 			.append("CC   Switzerland\n")
615 			.append("CC\n")
616 			.append("CC   Email: enzyme@isb-sib.ch\n")
617 			.append("CC\n")
618 			.append("CC   WWW server: http://enzyme.expasy.org/\n")
619 			.append("CC\n")
620 			.append("CC   -----------------------------------------------------------------------\n")
621 			.append("CC   Copyrighted by the SIB Swiss Institute of Bioinformatics. \n")
622 			.append("CC   There are no restrictions on its use by any institutions as long as\n")
623 			.append("CC   its content is in no way modified.\n")
624 			.append("CC   -----------------------------------------------------------------------\n")
625 			.append("//\n");
626       header = enzymeHeader.toString();
627     }
628 
629     return header;
630   }
631 
632   /**
633    * Inserts line breaks for the given line type if the given text is longer than
634    * {@link LineFormatter#LINEWIDTH 78} characters.
635    *
636    * @param nonWrappedText The text following the line type.
637    * @param lineType       The line type of the line to be wrapped.
638    * @return the wrapped line including the line type information.
639    * @throws SPTRException        if an error occurred during the line breaking process.
640    * @throws NullPointerException if any of the parameters is <code>null</code>.
641    */
642   public static String insertLineBreaks(String nonWrappedText, LineType lineType) throws SPTRException {
643     if (nonWrappedText == null) throw new NullPointerException("Parameter nonWrappedText == null not allowed here.");
644     if (lineType == null) throw new NullPointerException("Parameter lineType == null not allowed here.");
645 
646     LineFormatter lineWrapper;
647 
648     // CC lines are treated differently.
649     if (lineType == LineType.CC) {
650       lineWrapper = new CC_LineFormatter();
651     } else {
652       lineWrapper = new DefaultLineFormatter();
653     }
654 
655     return lineWrapper.formatLines(nonWrappedText, lineType);
656   }
657 }