diff options
Diffstat (limited to 'jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/XMLStringEncoder.java')
-rw-r--r-- | jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/XMLStringEncoder.java | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/XMLStringEncoder.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/XMLStringEncoder.java new file mode 100644 index 0000000000..fd637ebfa9 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/XMLStringEncoder.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 Oracle. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0, which accompanies this distribution + * and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Contributors: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +/** + * This encoder will replace any of a specified set of characters with an XML + * "character reference": '/' => "/" + */ +public final class XMLStringEncoder { + + /** The set of characters to be converted into XML character references. */ + private final char[] chars; + + /** Cache the value of the highest character in the set above. */ + private final char maxChar; + + + // ********** constructors/initialization ********** + + /** + * Construct an encoder that converts the specified set of characters + * into XML character references. + */ + public XMLStringEncoder(char[] chars) { + super(); + if (chars == null) { + throw new NullPointerException(); + } + // the ampersand must be included since it is the escape character + if (CollectionTools.contains(chars, '&')) { + this.chars = chars; + } else { + this.chars = CollectionTools.add(chars, '&'); + } + this.maxChar = this.calculateMaxInvalidFileNameChar(); + } + + /** + * Calculate the maximum value of the set of characters to be converted + * into XML character references. This will be used to short-circuit the + * search for a character in the set. + * @see #charIsToBeEncoded(char) + */ + private char calculateMaxInvalidFileNameChar() { + char[] localChars = this.chars; + char max = 0; + for (int i = localChars.length; i-- > 0; ) { + char c = localChars[i]; + if (max < c) { + max = c; + } + } + return max; + } + + + // ********** API ********** + + /** + * Return the specified string with any characters in the set + * replaced with XML character references. + */ + public String encode(String s) { + int len = s.length(); + // allow for a few encoded characters + StringBuffer sb = new StringBuffer(len + 20); + for (int i = 0; i < len; i++) { + this.appendCharacterTo(s.charAt(i), sb); + } + return sb.toString(); + } + + /** + * Return the specified string with any XML character references + * replaced by the characters themselves. + */ + public String decode(String s) { + StringBuffer sb = new StringBuffer(s.length()); + StringBuffer temp = new StringBuffer(); // performance tweak + this.decodeTo(new StringReader(s), sb, temp); + return sb.toString(); + } + + + // ********** internal methods ********** + + /** + * Append the specified character to the string buffer, + * converting it to an XML character reference if necessary. + */ + private void appendCharacterTo(char c, StringBuffer sb) { + if (this.charIsToBeEncoded(c)) { + this.appendCharacterReferenceTo(c, sb); + } else { + sb.append(c); + } + } + + /** + * Return whether the specified character is one of the characters + * to be converted to XML character references. + */ + private boolean charIsToBeEncoded(char c) { + return (c <= this.maxChar) && CollectionTools.contains(this.chars, c); + } + + /** + * Append the specified character's XML character reference to the + * specified string buffer (e.g. '/' => "/"). + */ + private void appendCharacterReferenceTo(char c, StringBuffer sb) { + sb.append("&#x"); + sb.append(Integer.toString(c, 16)); + sb.append(';'); + } + + private void decodeTo(Reader reader, StringBuffer sb, StringBuffer temp) { + try { + this.decodeTo_(reader, sb, temp); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private void decodeTo_(Reader reader, StringBuffer sb, StringBuffer temp) throws IOException { + int c = reader.read(); + while (c != -1) { + if (c == '&') { + this.decodeCharacterReferenceTo(reader, sb, temp); + } else { + sb.append((char) c); + } + c = reader.read(); + } + reader.close(); + } + + private void decodeCharacterReferenceTo(Reader reader, StringBuffer sb, StringBuffer temp) throws IOException { + int c = reader.read(); + this.checkChar(c, '#'); + c = reader.read(); + this.checkChar(c, 'x'); + + temp.setLength(0); + c = reader.read(); + while (c != ';') { + this.checkEndOfStream(c); + temp.append((char) c); + c = reader.read(); + } + String charValue = temp.toString(); + if (charValue.length() == 0) { + throw new IllegalStateException("missing numeric string"); + } + sb.append((char) Integer.parseInt(charValue, 16)); + } + + private void checkChar(int c, int expected) { + this.checkEndOfStream(c); + if (c != expected) { + throw new IllegalStateException("expected '" + (char) expected + "', but encountered '" + (char) c + "'"); + } + } + + private void checkEndOfStream(int c) { + if (c == -1) { + throw new IllegalStateException("unexpected end of string"); + } + } + +} |