001: /*---------------------------------------------------------------------------*\
002: $Id: CurnUtil.java 7041 2007-09-09 01:04:47Z bmc $
003: ---------------------------------------------------------------------------
004: This software is released under a BSD-style license:
005:
006: Copyright (c) 2004-2007 Brian M. Clapper. All rights reserved.
007:
008: Redistribution and use in source and binary forms, with or without
009: modification, are permitted provided that the following conditions are
010: met:
011:
012: 1. Redistributions of source code must retain the above copyright notice,
013: this list of conditions and the following disclaimer.
014:
015: 2. The end-user documentation included with the redistribution, if any,
016: must include the following acknowlegement:
017:
018: "This product includes software developed by Brian M. Clapper
019: (bmc@clapper.org, http://www.clapper.org/bmc/). That software is
020: copyright (c) 2004-2007 Brian M. Clapper."
021:
022: Alternately, this acknowlegement may appear in the software itself,
023: if wherever such third-party acknowlegements normally appear.
024:
025: 3. Neither the names "clapper.org", "curn", nor any of the names of the
026: project contributors may be used to endorse or promote products
027: derived from this software without prior written permission. For
028: written permission, please contact bmc@clapper.org.
029:
030: 4. Products derived from this software may not be called "curn", nor may
031: "clapper.org" appear in their names without prior written permission
032: of Brian M. Clapper.
033:
034: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
035: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
036: MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
037: NO EVENT SHALL BRIAN M. CLAPPER BE LIABLE FOR ANY DIRECT, INDIRECT,
038: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
039: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
040: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
041: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
042: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
043: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
044: \*---------------------------------------------------------------------------*/
045:
046: package org.clapper.curn;
047:
048: import java.util.ResourceBundle;
049: import java.util.Locale;
050: import java.util.MissingResourceException;
051:
052: import java.net.URL;
053: import java.net.MalformedURLException;
054:
055: import java.io.File;
056: import java.io.FileOutputStream;
057: import java.io.FileWriter;
058: import java.io.IOException;
059: import java.io.OutputStreamWriter;
060: import java.io.PrintWriter;
061: import java.io.Writer;
062:
063: import org.clapper.util.io.FileUtil;
064: import org.clapper.util.io.IOExceptionExt;
065: import org.clapper.util.io.RollingFileWriter;
066: import org.clapper.util.io.WordWrapWriter;
067:
068: import org.clapper.util.logging.Logger;
069:
070: /**
071: * Miscellaneous utility methods that are shared among classes,
072: * but don't logically belong anywhere in particular.
073: *
074: * @version <tt>$Revision: 7041 $</tt>
075: */
076: public class CurnUtil {
077: /*----------------------------------------------------------------------*\
078: Public Inner Classes
079: \*----------------------------------------------------------------------*/
080:
081: /**
082: * Constants defining where rolled file indicators should go in the
083: * file name pattern.
084: */
085: public enum IndexMarker {
086: BEFORE_EXTENSION, AFTER_EXTENSION
087: };
088:
089: /*----------------------------------------------------------------------*\
090: Public Constants
091: \*----------------------------------------------------------------------*/
092:
093: /*----------------------------------------------------------------------*\
094: Private Data Items
095: \*----------------------------------------------------------------------*/
096:
097: /**
098: * For error messages
099: */
100: private static WordWrapWriter err = new WordWrapWriter(System.err,
101: 78);
102:
103: /**
104: * For log messages
105: */
106: private static final Logger log = new Logger(CurnUtil.class);
107:
108: /*----------------------------------------------------------------------*\
109: Constructor
110: \*----------------------------------------------------------------------*/
111:
112: private CurnUtil() {
113: // Cannot be instantiated.
114: }
115:
116: /*----------------------------------------------------------------------*\
117: Public Methods
118: \*----------------------------------------------------------------------*/
119:
120: /**
121: * Normalize a URL, by forcing its host name and protocol to lower
122: * case.
123: *
124: * @param url The URL to normalize.
125: *
126: * @return a new <tt>URL</tt> object representing the normalized URL
127: *
128: * @see #normalizeURL(String)
129: */
130: public static URL normalizeURL(URL url) {
131: try {
132: String protocol = url.getProtocol().toLowerCase();
133: String host = url.getHost().toLowerCase();
134: int port = url.getPort();
135: String file = url.getFile();
136: String ref = url.getRef();
137:
138: if ((ref != null) && (ref.length() > 0))
139: file = file + "#" + ref;
140:
141: url = new URL(protocol, host, port, file);
142: }
143:
144: catch (MalformedURLException ex) {
145: // Shouldn't happen
146: }
147:
148: return url;
149: }
150:
151: /**
152: * Normalize a URL, by forcing its host name and protocol to lower
153: * case.
154: *
155: * @param url The URL to normalize, as a string
156: *
157: * @return a new <tt>URL</tt> object representing the normalized URL
158: *
159: * @see #normalizeURL(URL)
160: *
161: * @throws MalformedURLException bad URL string
162: */
163: public static URL normalizeURL(final String url)
164: throws MalformedURLException {
165: return normalizeURL(new URL(url));
166: }
167:
168: /**
169: * Convert a URL to a lookup key, by normalizing it and converting it
170: * to a string. Calling this method ensures that everyone converts a
171: * URL to a key the same way.
172: *
173: * @param url the URL
174: *
175: * @return the lookup key (really, the normalized, stringified URL)
176: */
177: public static String urlToLookupKey(final URL url) {
178: return CurnUtil.normalizeURL(url).toExternalForm();
179: }
180:
181: /**
182: * Get the resource bundle.
183: *
184: * @param locale the locale to use, or null for the default
185: *
186: * @return the resource bundle
187: *
188: * @see Constants#BUNDLE_NAME
189: * @see #getResourceFromBundle
190: */
191: public static ResourceBundle getResourceBundle(Locale locale) {
192: if (locale == null)
193: locale = Locale.getDefault();
194:
195: return ResourceBundle.getBundle(Constants.BUNDLE_NAME, locale);
196: }
197:
198: /**
199: * Get a string (resource) from the resource bundle.
200: *
201: * @param key the key for the resource to look up
202: * @param locale the locale to use, or null for the default
203: *
204: * @return the resource bundle, or null if the resource doesn't exist
205: *
206: * @see Constants#BUNDLE_NAME
207: * @see #getResourceBundle
208: */
209: public static String getResourceFromBundle(final String key,
210: final Locale locale) {
211: String result = null;
212:
213: try {
214: result = getResourceBundle(locale).getString(key);
215: }
216:
217: catch (MissingResourceException ex) {
218: }
219:
220: return result;
221: }
222:
223: /**
224: * Transform a <tt>File</tt> object into a <tt>RollingFileWriter</tt>
225: * pattern.
226: *
227: * @param file the file
228: * @param indexMarkerLoc where the <tt>RollingFileWriter</tt> index marker
229: * should go
230: *
231: * @return the transformed path string
232: */
233: public static String makeRollingFileWriterPattern(final File file,
234: final IndexMarker indexMarkerLoc) {
235: // Transform the parameter into a pattern suitable for use by a
236: // RollingFileWriter. Split the file name into its base name and
237: // extension, and put the number after the base name. If there's no
238: // extension, just put it at the end.
239:
240: StringBuilder buf = new StringBuilder();
241: String path = file.getPath();
242:
243: switch (indexMarkerLoc) {
244: case BEFORE_EXTENSION:
245: String fileNoExt = FileUtil.getFileNameNoExtension(path);
246: String ext = FileUtil.getFileNameExtension(path);
247: buf.append(fileNoExt);
248: buf.append(RollingFileWriter.INDEX_PATTERN);
249: if (ext != null) {
250: buf.append(".");
251: buf.append(ext);
252: }
253: break;
254:
255: case AFTER_EXTENSION:
256: buf.append(path);
257: buf.append(RollingFileWriter.INDEX_PATTERN);
258: break;
259:
260: default:
261: assert (false);
262: }
263:
264: return buf.toString();
265: }
266:
267: /**
268: * Create a temporary file for XML content.
269: *
270: * @return the temp file
271: *
272: * @throws IOException error creating temporary file
273: */
274: public static File createTempXMLFile() throws IOException {
275: File f = File.createTempFile("curn", ".xml", null);
276: f.deleteOnExit();
277: return f;
278: }
279:
280: /**
281: * Open a file that might require backing up. Takes care of transforming
282: * the file name into a <tt>RollingFileWriter</tt> pattern, if necessary.
283: *
284: * @param file the file to open
285: * @param encoding encoding to use when opening the file, or null
286: * for the default
287: * @param totalBackups total backups to keep, if positive
288: * @param indexMarkerLoc where the <tt>RollingFileWriter</tt> index marker
289: * should go. Ignored unless <tt>totalBackups</tt>
290: * is positive.
291: *
292: * @return a <tt>PrintWriter</tt> for the output
293: *
294: * @throws IOExceptionExt on error
295: */
296: public static Writer openOutputFile(final File file,
297: final String encoding, final IndexMarker indexMarkerLoc,
298: final int totalBackups) throws IOExceptionExt {
299: try {
300: Writer w;
301:
302: if (totalBackups != 0) {
303: String pattern = CurnUtil.makeRollingFileWriterPattern(
304: file, indexMarkerLoc);
305: log.debug("Opening rolling output file \"" + pattern
306: + "\"");
307: w = new RollingFileWriter(pattern, encoding,
308: /* max size = */0,
309: /* max files = */totalBackups);
310: }
311:
312: else {
313: log.debug("Opening non-rolling output file \""
314: + file.getPath() + "\"");
315: if (encoding != null) {
316: w = new OutputStreamWriter(new FileOutputStream(
317: file), encoding);
318: }
319:
320: else {
321: w = new PrintWriter(new FileWriter(file));
322: }
323: }
324:
325: return w;
326: }
327:
328: catch (IOException ex) {
329: throw new IOExceptionExt(Constants.BUNDLE_NAME,
330: "Util.cantOpenFile",
331: "Unable to open file \"{0}\" for output",
332: new Object[] { file.getPath() }, ex);
333: }
334: }
335:
336: /**
337: * Get a <tt>PrintWriter</tt> for writing error messages to the screen.
338: *
339: * @return a suitable <tt>PrintWriter</tt>
340: */
341: public static PrintWriter getErrorOut() {
342: return err;
343: }
344:
345: /**
346: * Map a configured path name to a <tt>File</tt> object. This method parses
347: * the path name and converts any Unix-style path separators to the
348: * appropriate separator for the current platform. Use of this method
349: * allows Unix-style paths in the configuration file, even on non-Unix
350: * systems (which also bypasses some parsing issues).
351: *
352: * @param pathName the path name to map
353: *
354: * @return an appropriate <tt>File</tt> object for the current system.
355: */
356: public static File mapConfiguredPathName(final String pathName) {
357: File result = null;
358:
359: if (File.separatorChar == '/') {
360: // Nothing to do.
361:
362: result = new File(pathName);
363: }
364:
365: else {
366: char[] ch = pathName.toCharArray();
367: StringBuilder buf = new StringBuilder();
368: for (int i = 0; i < ch.length; i++) {
369: if (ch[i] == '/')
370: buf.append(File.separatorChar);
371: else
372: buf.append(ch[i]);
373: }
374:
375: result = new File(buf.toString());
376: }
377:
378: return result;
379: }
380: }
|