001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.util;
011:
012: import java.util.*;
013:
014: import org.mmbase.util.logging.Logger;
015: import org.mmbase.util.logging.Logging;
016: import org.mmbase.util.transformers.*;
017:
018: /**
019: *
020: * Class to convert from/to a string (byte[]) from/to a encoded string (byte[])
021: *
022: * Supported encodings are at this moment:
023: * <ul>
024: * <li>BASE64</li>
025: * <li>HEX</li>
026: * <li>ESCAPE_XML</li>
027: * <li>ESCAPE_HTML</li>
028: * <li>ESCAPE_HTML_ATTRIBUTE</li>
029: * <li>ESCAPE_WML</li>
030: * <li>ESCAPE_WML_ATTRIBUTE</li>
031: * <li>ESCAPE_URL</li>
032: * <li>ESCAPE_URL_PARAM</li>
033: * <li>ESCAPE_SINGLE_QUOTE</li>
034: * </ul>
035: *
036: * A list of supported encodings can be gotten by java
037: * org.mmbase.util.Encode, and you add your own encodings by calling
038: * the static function 'register' of this class.
039: *
040: * Usage:
041: * <pre>
042: * Encode encoder = new Encode("ESCAPE_XML");
043: * System.out.println( encoder.decode( encoder.encode("& \" < >") ) );
044: * </pre>
045: *
046: * @rename Encoder
047: * @author Eduard Witteveen
048: * @author Michiel Meeuwissen
049: * @version $Id: Encode.java,v 1.28 2007/02/24 21:57:50 nklasens Exp $
050: **/
051: public class Encode {
052:
053: private static Logger log;
054:
055: private Transformer trans; // the instance of the object doing the actual work.
056:
057: private static Map<String, Config> encodings; // string -> Config, all encoding are registered in this.
058: private static Set<String> registered = new HashSet<String>(); // in this is remembered which classes were registered, to avoid registering them more than once.
059:
060: static {
061: log = Logging.getLoggerInstance(Encode.class);
062: encodings = new HashMap<String, Config>();
063:
064: // a few Encoding are avaible by default:
065: try {
066: register("org.mmbase.util.transformers.MD5");
067: register("org.mmbase.util.transformers.Base64");
068: register("org.mmbase.util.transformers.Hex");
069: register("org.mmbase.util.transformers.Xml");
070: register("org.mmbase.util.transformers.Url");
071: register("org.mmbase.util.transformers.Sql");
072: register("org.mmbase.util.transformers.XmlField");
073: register("org.mmbase.util.transformers.LinkFinder");
074: register("org.mmbase.util.transformers.Censor");
075: register("org.mmbase.util.transformers.Rot13");
076: register("org.mmbase.util.transformers.Rot5");
077: register("org.mmbase.util.transformers.UnicodeEscaper");
078: } catch (IllegalArgumentException e) {
079: log.warn("", e);
080: }
081: }
082:
083: /**
084: * Created a encode instance of a certain type of encoding. It
085: * will instantiate the right class, and configure it. This
086: * instantion will be used when you call 'encode' or 'decode'
087: * members later.
088: *
089: * @param encoding a string that describes which encoding should be used.
090: */
091: public Encode(String encoding) {
092: if (encodings.containsKey(encoding.toUpperCase())) { // it must be known.
093: Config e = encodings.get(encoding.toUpperCase()); // get the info.
094: try {
095: trans = (Transformer) e.clazz.newInstance();
096: } catch (InstantiationException ex) {
097: throw new IllegalArgumentException("encoding: '"
098: + encoding + "' could not be instantiated");
099: } catch (IllegalAccessException ex) {
100: }
101: if (trans instanceof ConfigurableTransformer) {
102: ((ConfigurableTransformer) trans).configure(e.config);
103: }
104: } else {
105: throw new IllegalArgumentException("encoding: '" + encoding
106: + "' unknown" + encodings.keySet());
107: }
108:
109: }
110:
111: /**
112: * @since MMBase-1.8
113: */
114: public Transformer getTransformer() {
115: return trans;
116: }
117:
118: /**
119: * Add new transformation types. Feed it with a class name (which
120: * must implement Transformer)
121: *
122: * @param clazz a class name.
123: */
124: public static void register(String clazz) {
125: if (!registered.contains(clazz)) { // if already registered, do nothing.
126: log.service("registering encode class " + clazz);
127: try {
128: Class<?> atrans = Class.forName(clazz);
129: if (Transformer.class.isAssignableFrom(atrans)) { // make sure it is of the right type.
130: if (ConfigurableTransformer.class
131: .isAssignableFrom(atrans)) {
132: log.debug("A configurable transformer");
133: // Instantiate it, just once, to call the method 'transformers'
134: // In this way we find out what this class can do.
135: ConfigurableTransformer transformer = (ConfigurableTransformer) atrans
136: .newInstance();
137: Map<String, Config> newencodings = transformer
138: .transformers();
139: encodings.putAll(newencodings); // add them all to our encodings.
140: } else {
141: log.debug("Non configurable");
142: Transformer transformer = (Transformer) atrans
143: .newInstance();
144: encodings.put(transformer.toString()
145: .toUpperCase(), new Config(atrans, -1,
146: "Transformer: " + clazz));
147: }
148: // TODO, perhaps there should be a check here, to make sure that no two classes use the
149: // same string to identify a transformation.
150:
151: } else {
152: throw new IllegalArgumentException("The class "
153: + clazz + " does not implement "
154: + Transformer.class.getName());
155: }
156: } catch (ClassNotFoundException e) {
157: throw new IllegalArgumentException(e.toString());
158: } catch (Exception e) { // yeah, yeah, it can throw a lot more.
159: // TODO perhaps make better distinction between exceptions...
160: throw new IllegalArgumentException(e.toString());
161: }
162: registered.add(clazz);
163: }
164: }
165:
166: /**
167: * This function will encode a given string to it's encoded
168: * variant. It is static, it will make a temporary instance of
169: * the Transformer class. If you need to encode alot, it is
170: * better to use new Encode first.
171: *
172: *
173: * @param encoding a string that describes which encoding should be used.
174: * @param toEncode a string which is the value which should be encoded.
175: * This can also be a byte[].
176: *
177: * @return a string which is the encoded representation of toEncode
178: * with the given encoding
179: **/
180: public static String encode(String encoding, String toEncode) {
181: Encode e = new Encode(encoding);
182: return e.encode(toEncode);
183: }
184:
185: public static String encode(String encoding, byte[] bytes) {
186: Encode e = new Encode(encoding);
187: return e.encode(bytes);
188: }
189:
190: /**
191: * This function will decode a given string to it's decoded variant.
192: * @see #encode
193: * @param encoding a string that describes which decoding should be used.
194: * @param toDecode a string which is the value which should be encoded.
195: * @return a string which is the encoded representation of toEncode
196: * with the given encoding
197: **/
198:
199: public static String decode(String encoding, String toDecode) {
200: Encode e = new Encode(encoding);
201: return e.decode(toDecode);
202: }
203:
204: public static byte[] decodeBytes(String encoding, String toDecode) {
205: Encode e = new Encode(encoding);
206: return e.decodeBytes(toDecode);
207: }
208:
209: /**
210: * This function will encode a given string to it's encoded variant.
211: * @param toEncode A string which is the value which should be encoded.
212: If the transformer does transform bytes, then first getBytes is done on the String.
213: *
214: * @return a string which is the encoded representation of toEncode
215: * with the given encoding
216: **/
217: public String encode(String toEncode) {
218: if (isByteToCharEncoder()) {
219: return ((ByteToCharTransformer) trans).transform(toEncode
220: .getBytes());
221: } else {
222: return ((CharTransformer) trans).transform(toEncode);
223: }
224: }
225:
226: /**
227: * Encodes a byte array.
228: *
229: * @return a string;;
230: */
231: public String encode(byte[] bytes) {
232: return ((ByteToCharTransformer) trans).transform(bytes);
233: }
234:
235: /**
236: * This function will decode a given string to it's decoded variant
237: * @param toDecode a string which is the value which should be encoded.
238: * @return a string which is the encoded representation of toEncode
239: * with the given encoding
240: **/
241: public String decode(String toDecode) {
242: if (isByteToCharEncoder()) {
243: return new String(((ByteToCharTransformer) trans)
244: .transformBack(toDecode));
245: } else {
246: return ((CharTransformer) trans).transformBack(toDecode);
247: }
248: }
249:
250: public byte[] decodeBytes(String toDecode) {
251: if (isByteToCharEncoder()) {
252: return ((ByteToCharTransformer) trans)
253: .transformBack(toDecode);
254: } else {
255: return ((CharTransformer) trans).transformBack(toDecode)
256: .getBytes();
257: }
258: }
259:
260: /**
261: * All the currently known encodings.
262: *
263: * @return Set of Strings containing the names of the registered encodings.
264: */
265:
266: public static Set<String> possibleEncodings() {
267: return encodings.keySet();
268: }
269:
270: /**
271: * Checks if a certain string represents a known transformation.
272: *
273: */
274: public static boolean isEncoding(String e) {
275: return encodings.containsKey(e.toUpperCase());
276: }
277:
278: /**
279: * Checks if the transformation is between two Strings.
280: */
281: public boolean isCharEncoder() {
282: return trans instanceof org.mmbase.util.transformers.CharTransformer;
283: }
284:
285: /**
286: * Checks if the transformations makes from byte[] String.
287: */
288: public boolean isByteToCharEncoder() {
289: return trans instanceof org.mmbase.util.transformers.ByteToCharTransformer;
290: }
291:
292: /**
293: * Returns the encoding
294: *
295: * @return An String representing the coding that is currently used.
296: */
297: public String getEncoding() {
298: return trans.toString();
299: }
300:
301: /**
302: * Invocation of the class from the commandline for testing.
303: */
304: public static void main(String[] argv) {
305: try {
306: org.mmbase.module.core.MMBaseContext.init(System
307: .getProperty("mmbase.config"), false);
308: } catch (Throwable e) {
309: System.err
310: .println("Could not intialize mmbase context, proceeding without it: "
311: + e.getMessage());
312: }
313: String coding = null;
314: boolean decode = false;
315: String string = null;
316:
317: { // read arguments.
318: int cur = 0;
319: while (cur < argv.length) {
320: if ("-decode".equals(argv[cur])) {
321: decode = true;
322: } else if ("-encode".equals(argv[cur])) {
323: } else if ("-class".equals(argv[cur])) {
324: register(argv[++cur]);
325: } else {
326: if (coding == null) {
327: coding = argv[cur];
328: if (!isEncoding(coding)) {
329: throw new RuntimeException(coding
330: + " is not a known coding");
331: }
332: } else if (argv[cur].charAt(0) == '-') {
333: throw new RuntimeException("unknown option "
334: + argv[cur]);
335: } else {
336: if (string == null)
337: string = "";
338: string += " " + argv[cur];
339: }
340: }
341: cur++;
342: }
343: }
344:
345: if (coding == null) { // supply help
346: System.out
347: .println("org.mmbase.util.Encode main is for testing purposes only\n");
348: System.out
349: .println(" use: java -Dmmbase.config=... org.mmbase.util.Encode [-class <classname> [-class ..]] [-encode|-decode] <coding> [string]\n\n");
350: System.out
351: .println("On default it encodes and gets the string from STDIN\n\n");
352: System.out.println("possible decoding are");
353: List<String> v = new ArrayList<String>(possibleEncodings());
354: java.util.Collections.sort(v);
355: Iterator<String> i = v.iterator();
356: while (i.hasNext()) {
357: String enc = i.next();
358: System.out.println(enc + " "
359: + encodings.get(enc).info);
360: }
361: } else {
362:
363: if (string == null) {
364: // put STDIN in the string.
365: string = "";
366: try {
367: java.io.BufferedReader stdinReader = new java.io.BufferedReader(
368: new java.io.InputStreamReader(System.in));
369: String line = stdinReader.readLine();
370: while (line != null) {
371: string += line + "\n";
372: line = stdinReader.readLine();
373: }
374: log.service("----------------");
375: } catch (java.io.IOException e) {
376: System.err.println(e.toString());
377: }
378: }
379:
380: // do the job:
381: if (decode) {
382: System.out.println(new String(decodeBytes(coding,
383: string)));
384: // decode bytes, then also byte decoding go ok... (I think).
385: } else {
386: System.out.println(encode(coding, string));
387: }
388: }
389: }
390: }
|