View Javadoc

1   package uk.ac.cam.spectra;
2   
3   import java.io.File;
4   import java.io.FileInputStream;
5   import java.io.FileOutputStream;
6   import java.io.IOException;
7   import java.io.OutputStream;
8   import java.nio.channels.FileChannel;
9   import java.text.DateFormat;
10  import java.text.ParseException;
11  import java.text.SimpleDateFormat;
12  import java.util.Date;
13  import java.util.regex.Pattern;
14  
15  import nu.xom.Attribute;
16  import nu.xom.Document;
17  import nu.xom.Nodes;
18  import nu.xom.Serializer;
19  import nu.xom.XPathContext;
20  
21  import org.apache.commons.io.IOUtils;
22  import org.apache.log4j.Logger;
23  
24  /**
25   * Usual grab bag of utility functions that don't fit anywhere else
26   * conveniently.
27   * 
28   * @author Jim Downing
29   */
30  public class Util {
31  	private static final int DEFAULT_LINE_LENGTH = 80;
32  
33  	private static final int DEFAULT_INDENT = 4;
34  
35  	private static final String DEFAULT_XML_ENCODING = "UTF-8";
36  
37  	private static final int PATH_SECTION_SIZE_EST = 20;
38  
39  	private static final Pattern PATHOLOGICAL_DATE_PATTERN = Pattern
40  			.compile("\\d{2}-\\d{2}-\\d{4}");
41  
42  	private static final Logger LOG = Logger.getLogger(Util.class);
43  
44  	private static final DateFormat[] DATE_FORMATS = {
45  			new SimpleDateFormat("dd/MM/yy"),
46  			new SimpleDateFormat("dd/MM/yyyy"),
47  			Constants.SIMPLE_ISO_DATE_FORMAT, DateFormat.getInstance() };
48  
49  	static {
50  		for (DateFormat df : DATE_FORMATS) {
51  			df.setLenient(false);
52  		}
53  	}
54  
55  	/**
56  	 * Utility class, instantiation is not expected.
57  	 */
58  	private Util() {
59  		;
60  	}
61  
62  	/**
63  	 * Builds a platform specific file path from the path name parts passed in.
64  	 * An obvious thing to do now we have varargs - why hasn't java.io.File got
65  	 * this?
66  	 * 
67  	 * @param parts
68  	 *            directory names and an optional file name
69  	 * @return platform specific path string.
70  	 */
71  	public static String buildPath(String... parts) {
72  		StringBuilder sb = new StringBuilder(parts.length
73  				* PATH_SECTION_SIZE_EST);
74  		for (String part : parts) {
75  			sb.append(part).append(File.separatorChar);
76  		}
77  		sb.deleteCharAt(sb.length() - 1);
78  		return sb.toString();
79  	}
80  
81  	/**
82  	 * DEWISOTT.
83  	 * 
84  	 * @param from
85  	 *            source file
86  	 * @param to
87  	 *            target file
88  	 * @throws IOException
89  	 * @throws IOException
90  	 *             rethrown from java.io.
91  	 */
92  	public static void copy(File from, File to) throws IOException {
93  		if (!to.exists()) {
94  			to.createNewFile();
95  		}
96  		FileInputStream fileInputStream = null;
97  		FileChannel srcChannel = null;
98  		FileOutputStream fileOutputStream = null;
99  		FileChannel dstChannel = null;
100 		try {
101 			fileInputStream = new FileInputStream(from);
102 			srcChannel = fileInputStream.getChannel();
103 			fileOutputStream = new FileOutputStream(to);
104 			dstChannel = fileOutputStream.getChannel();
105 			dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
106 		} finally {
107 			if (srcChannel != null) {
108 				try {
109 					srcChannel.close();
110 				} catch (IOException e) {
111 					LOG.warn(e);
112 				}
113 			}
114 			if (dstChannel != null) {
115 				try {
116 					dstChannel.close();
117 				} catch (IOException e) {
118 					LOG.warn(e);
119 				}
120 			}
121 			IOUtils.closeQuietly(fileInputStream);
122 			IOUtils.closeQuietly(fileOutputStream);
123 		}
124 	}
125 
126 	/**
127 	 * Yet another method that java.io.File should possess, performs an atomic
128 	 * move if possible, otherwise a non-atomic one.
129 	 * 
130 	 * @param from
131 	 *            source file
132 	 * @param to
133 	 *            target file
134 	 * @throws IOException
135 	 *             rethrown from java.io
136 	 */
137 	public static void move(File from, File to) throws IOException {
138 		if (!from.renameTo(to)) {
139 			copy(from, to);
140 			from.delete();
141 		}
142 	}
143 
144 	/**
145 	 * Utility method to return the result of an xpath query you know is going
146 	 * to return a single text node.
147 	 * 
148 	 * @param doc
149 	 *            XOM document to query
150 	 * @param query
151 	 *            the XPath query
152 	 * @param xpathContext
153 	 *            the XPath context to resolve namespaces
154 	 * @throws RuntimeException
155 	 *             if the query doesn't return a single node, or if the node
156 	 *             returned is not a text node.
157 	 * @return String of Text node, untrimmed.
158 	 */
159 	public static Attribute queryUniqueAttribute(Document doc, String query,
160 			XPathContext xpathContext) {
161 		Nodes nds = doc.query(query, xpathContext);
162 		if (nds.size() != 1) {
163 			throw new RuntimeException("Query was not successful and unique. "
164 					+ query + " returned " + nds.size());
165 		}
166 		Object obj = nds.get(0);
167 		if (!(obj instanceof Attribute)) {
168 			throw new RuntimeException(
169 					"Query result was not an Attribute node: " + obj);
170 		}
171 		return (Attribute) obj;
172 	}
173 
174 	/**
175 	 * Prints a XOM document to an outputstream without having to remember the
176 	 * serializer voodoo.
177 	 * 
178 	 * @param doc
179 	 *            the XOM DOcument to print
180 	 * @param out
181 	 *            where to print to
182 	 * @param pretty
183 	 *            should it be pretty printed?
184 	 * @throws IOException
185 	 *             rethrown from the XOM serializer
186 	 */
187 	public static void print(Document doc, OutputStream out, boolean pretty) {
188 		Serializer serializer;
189 		try {
190 			serializer = new Serializer(out, DEFAULT_XML_ENCODING);
191 			if (pretty) {
192 				serializer.setIndent(DEFAULT_INDENT);
193 				serializer.setMaxLength(DEFAULT_LINE_LENGTH);
194 			}
195 			serializer.write(doc);
196 		} catch (Exception e) {
197 			LOG.error(e);
198 			throw new RuntimeException(e);
199 		}
200 	}
201 
202 	/**
203 	 * Parse a date according to a number of standard formats.
204 	 * 
205 	 * @param dateStr
206 	 *            the date as a string
207 	 * @return a parsed date object, or null if none of the standard formats can
208 	 *         parse the string.
209 	 */
210 	public static Date parseDate(String dateStr) {
211 		// Trap the peculiar dd-MM-yyyy
212 		if (PATHOLOGICAL_DATE_PATTERN.matcher(dateStr).matches()) {
213 			return null;
214 		}
215 		Date d = null;
216 		for (DateFormat df : DATE_FORMATS) {
217 			try {
218 				d = df.parse(dateStr);
219 			} catch (ParseException e) {
220 				continue;
221 			}
222 			if (d != null) {
223 				LOG
224 						.debug("parsed "
225 								+ dateStr
226 								+ " using "
227 								+ (df instanceof SimpleDateFormat ? ((SimpleDateFormat) df)
228 										.toPattern()
229 										: df.toString()));
230 				break;
231 			}
232 		}
233 		return d;
234 	}
235 
236 	public static void rmr(File dir) {
237 		for (File f : dir.listFiles()) {
238 			if (f.isDirectory()) {
239 				rmr(f);
240 			} else {
241 				f.delete();
242 			}
243 		}
244 	}
245 
246 }