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 * Created on Sep 14, 2011 021 * Author: Amr ALHOSSARY, Richard Adams 022 * 023 */ 024package org.biojava.nbio.core.util; 025 026import java.io.BufferedReader; 027import java.io.ByteArrayInputStream; 028import java.io.IOException; 029import java.io.InputStream; 030import java.io.InputStreamReader; 031import java.util.Collection; 032import java.util.Scanner; 033import java.util.stream.Collectors; 034 035import javax.xml.parsers.DocumentBuilder; 036import javax.xml.parsers.DocumentBuilderFactory; 037import javax.xml.parsers.ParserConfigurationException; 038 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041import org.w3c.dom.Document; 042import org.w3c.dom.DocumentType; 043import org.w3c.dom.NamedNodeMap; 044import org.w3c.dom.Node; 045import org.xml.sax.SAXException; 046 047 048/** 049 * A utility class for common {@link String} manipulation tasks. 050 * All functions are static methods. 051 * 052 * @author Amr ALHOSSARY 053 * @author Richard Adams 054 */ 055public class StringManipulationHelper { 056 057 private final static Logger logger = LoggerFactory.getLogger(StringManipulationHelper.class); 058 059 /** 060 * we are using Unix endline here, since this is used for testing XML and it 061 * is part of the XML recommendations: <a href 062 * ="http://www.w3.org/TR/REC-xml/#sec-line-ends" 063 * >http://www.w3.org/TR/REC-xml/#sec-line-ends</a> 064 */ 065 private static final String UNIX_NEWLINE = "\n"; 066 067 private StringManipulationHelper() { 068 // to prevent instantiation 069 } 070 071 /** 072 * Converts an InputStream of text to a String, closing the stream 073 * before returning. 074 * <ul> 075 * <li> Newlines are converted to Unix newlines (\n) 076 * <li> Default charset encoding is used to read the stream. 077 * <li> Any IOException reading the stream is 'squashed' and not made 078 * available to caller 079 * <li> An additional newline is appended at the end of the string. 080 * <ul> 081 * @author andreas 082 * @param stream 083 * @return a possibly empty but non-null String 084 */ 085 public static String convertStreamToString(InputStream stream) { 086 BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); 087 StringBuilder sb = new StringBuilder(); 088 089 String line = null; 090 try { 091 while ((line = reader.readLine()) != null) { 092 sb.append(line).append(UNIX_NEWLINE); 093 } 094 } catch (IOException e) { 095 // logger.error("Exception: ", e); 096 } finally { 097 try { 098 stream.close(); 099 } catch (IOException e) { 100 logger.error("Exception: ", e); 101 } 102 } 103 return sb.toString(); 104 } 105 106 /** 107 * Compares two strings in a case-sensitive manner for equality, line by line, ignoring any difference 108 * of end line delimiters contained within the 2 Strings. 109 * <br/> 110 * This method should 111 * be used if and only if two Strings are considered identical when all nodes 112 * are identical including their relative order. Generally useful when 113 * asserting identity of <b>automatically regenerated</b> XML or PDB. 114 * 115 * @param expected 116 * @param actual 117 */ 118 public static boolean equalsToIgnoreEndline(String expected, String actual) { 119 if (expected == null && actual == null) { 120 return true; 121 } 122 if (expected != null ^ actual != null) { 123 return false; 124 } 125 Scanner scanner1 = new Scanner(expected); 126 Scanner scanner2 = new Scanner(actual); 127 String line1, line2; 128 while (scanner1.hasNextLine()) { 129 line1 = scanner1.nextLine(); 130 if(scanner2.hasNextLine()) { 131 line2 = scanner2.nextLine(); 132 if (! line1.equals(line2)) { 133 closeScanners(scanner1, scanner2); 134 return false; 135 } 136 } else { 137 closeScanners(scanner1, scanner2); 138 return false; 139 } 140 } 141 if (scanner2.hasNextLine()) { 142 closeScanners(scanner1, scanner2); 143 return false; 144 } 145 146 closeScanners(scanner1, scanner2); 147 return true; 148 } 149 150 private static void closeScanners(Scanner s1, Scanner s2) { 151 s1.close(); 152 s2.close(); 153 } 154 155 /** 156 * This method is not implemented or used, never returns true 157 * and should probably be removed. 158 * @param expected 159 * @param actual 160 * @return 161 * @throws UnsupportedOperationException in most cases 162 */ 163 public static boolean equalsToXml(String expected, String actual) { 164 Document expectedDocument=null; 165 Document actualDocument=null; 166 try { 167 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); 168 DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); 169 expectedDocument = documentBuilder.parse(new ByteArrayInputStream(expected.getBytes())); 170 actualDocument = documentBuilder.parse(new ByteArrayInputStream(actual.getBytes())); 171 } catch (ParserConfigurationException e) { 172 logger.error("Exception: ", e); 173 throw new RuntimeException("Couldn't Parse XML", e); 174 } catch (SAXException e) { 175 logger.error("Exception: ", e); 176 throw new RuntimeException("Couldn't Parse XML", e); 177 } catch (IOException e) { 178 logger.error("Exception: ", e); 179 throw new RuntimeException("Couldn't Parse XML", e); 180 } 181 final DocumentType doctype1 = expectedDocument.getDoctype(); 182 final DocumentType doctype2 = actualDocument.getDoctype(); 183 if (doctype1==null ^ doctype2 == null) { 184 return false; 185 }else if (doctype1!= null /*&& doctype2 != null*/) { 186 NamedNodeMap expectedNotations = doctype1.getNotations(); 187 NamedNodeMap actualNotations = doctype2.getNotations(); 188 if (expectedNotations.getLength() == actualNotations.getLength()) { 189 for (int i = 0; i < expectedNotations.getLength(); i++) { 190 Node node= expectedNotations.item(i); 191 node.isEqualNode(null); 192 } 193 }else{ 194 return false; 195 } 196 197 } 198 199 throw new UnsupportedOperationException("not yet implemented"); 200 } 201 202 /** 203 * Adds padding to left of supplied string 204 * @param s The String to pad 205 * @param n an integer >= 1 206 * @return The left-padded string. 207 * @throws IllegalArgumentException if n <= 0 208 */ 209 public static String padLeft(String s, int n) { 210 validatePadding(n); 211 return String.format("%1$" + n + "s", s); 212 } 213 214 /** 215 * Adds padding to right of supplied string 216 * @param s The String to pad 217 * @param n an integer >= 1 218 * @return The right-padded string. 219 * @throws IllegalArgumentException if n <= 0 220 */ 221 public static String padRight(String s, int n) { 222 validatePadding(n); 223 return String.format("%1$-" + n + "s", s); 224 } 225 226 private static void validatePadding(int n) { 227 if (n <=0 ) { 228 throw new IllegalArgumentException("padding must be >= 1"); 229 } 230 } 231 232 /** 233 * Joins Strings together with a delimiter to a single 234 * @param s An {@link Iterable} of Strings 235 * @param delimiter 236 * @return 237 */ 238 public static String join(Collection<String> s, String delimiter) { 239 if (s==null) return ""; 240 return s.stream().collect(Collectors.joining(delimiter)); 241 } 242 243}