/* You may freely copy, distribute, modify and use this class as long as the original author attribution remains intact. See message below. Copyright (C) 2001 Christian Pesch. All Rights Reserved. */ package slash.metamusic.mp3.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringReader; import java.util.BitSet; /** * This class provides a encoding scheme for file name strings. * An encoded string should be a valid path name on all target platforms. * * @author Christian Pesch * @version $Id: FileEncodingScheme.java 384 2004-10-15 15:21:10Z cpesch $ */ public class FileEncodingScheme { // used by encode() to determine which characters to escape: protected BitSet dontNeedEncoding = new BitSet(256); static final private int caseDiff = ('a' - 'A'); // character to use for escaping dangerous chars (must be a valid URL character, so don't use '%'): protected char escapeChar = '_'; public FileEncodingScheme() { int i; for (i = 'a'; i <= 'z'; i++) doesNotNeedEncoding(i); for (i = 'A'; i <= 'Z'; i++) doesNotNeedEncoding(i); for (i = '0'; i <= '9'; i++) doesNotNeedEncoding(i); doesNotNeedEncoding('&'); doesNotNeedEncoding('+'); doesNotNeedEncoding('-'); doesNotNeedEncoding('_'); doesNotNeedEncoding('.'); doesNotNeedEncoding(','); doesNotNeedEncoding(','); doesNotNeedEncoding('\''); doesNotNeedEncoding(' '); needsEncoding(escapeChar); } /** * Make sure that the given character will be not encoded in the * receivers encoding scheme */ public void doesNotNeedEncoding(char ch) { dontNeedEncoding.set(ch); } /** * Make sure that the given character will be not encoded in the * receivers encoding scheme */ public void doesNotNeedEncoding(int i) { doesNotNeedEncoding((char) i); } /** * Make sure that the given character will be encoded in the * receivers encoding scheme */ public void needsEncoding(char ch) { dontNeedEncoding.clear(ch); } /** * Encode the given String in such a way that it becomes a valid file name. * * @param s <code>String</code> to be translated. * @return the translated <code>String</code>. */ public String encode(String s) { byte[] bytes = s.getBytes(); StringBuffer out = new StringBuffer(bytes.length); for (int i = 0; i < bytes.length; i++) { int c = BitConversion.unsignedByteToInt(bytes[i]); if (dontNeedEncoding.get(c)) { out.append((char) c); } else if ((char) c == escapeChar) { out.append(escapeChar); out.append(escapeChar); } else { out.append(escapeChar); char ch = Character.forDigit((c >> 4) & 0xF, 16); // converting to use uppercase letter as part of // the hex value if ch is a letter. if (Character.isLetter(ch)) { ch -= caseDiff; } out.append(ch); ch = Character.forDigit(c & 0xF, 16); if (Character.isLetter(ch)) { ch -= caseDiff; } out.append(ch); } } return out.toString(); } /** * Reverse the effect of #encode() * * @param s <code>String</code> returned by #encode() * @return the string originally given to #encode(). */ public String decode(String s) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); StringReader reader = new StringReader(s); try { int c = reader.read(); while (c != -1) { int decoded; if (c == escapeChar) { int hi = reader.read(); if (hi == -1 || (char) hi == escapeChar) { decoded = c; } else { int lo = reader.read(); if (lo == -1) { baos.write(escapeChar); decoded = hi; } else { hi = Character.digit((char) hi, 16); lo = Character.digit((char) lo, 16); decoded = (hi << 4 | lo); } } } else { decoded = c; } baos.write(decoded); c = reader.read(); } return new String(baos.toByteArray()); } catch (IOException e) { // can not happen since no one else may closes this StringReader e.printStackTrace(); return null; } } }