Added ResXmlEncoders class and use it instead of encoding methods in AndrolibResources.

This commit is contained in:
Ryszard Wiśniewski 2011-05-09 11:23:16 +02:00
parent 30add278d5
commit e23bf8e70d
5 changed files with 148 additions and 101 deletions

View File

@ -27,7 +27,6 @@ import brut.androlib.res.util.*;
import brut.common.BrutException; import brut.common.BrutException;
import brut.directory.*; import brut.directory.*;
import brut.util.*; import brut.util.*;
import java.awt.event.KeyEvent;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -485,98 +484,6 @@ final public class AndrolibResources {
} }
} }
public static String escapeTextForResXml(String value) {
return escapeTextForResXml(value, true);
}
public static String escapeTextForResXml(String value,
boolean escapeChars) {
if (value.isEmpty()) {
return value;
}
if (escapeChars) {
value = escapeCharsForResXml(value);
}
StringBuilder out = new StringBuilder(value.length() + 10);
char[] chars = value.toCharArray();
switch (chars[0]) {
case '@':
case '#':
case '?':
out.append('\\');
}
boolean space = true;
for (char c : chars) {
if (c == ' ') {
if (space) {
out.append("\\u0020");
} else {
out.append(c);
space = true;
}
continue;
}
space = false;
out.append(c);
}
if (space && out.charAt(out.length() - 1) == ' ') {
out.deleteCharAt(out.length() - 1);
out.append("\\u0020");
}
return out.toString();
}
public static String escapeCharsForResXml(String value) {
if (value.isEmpty()) {
return value;
}
StringBuilder out = new StringBuilder(value.length() + 10);
for (char c : value.toCharArray()) {
switch (c) {
case '\\':
case '\'':
out.append('\\');
break;
case '"':
out.append("\\"");
continue;
case '\n':
out.append("\\n");
continue;
case '&':
out.append("&");
continue;
case '<':
out.append("&lt;");
continue;
default:
if (! isPrintableChar(c)) {
out.append(String.format("\\u%04x", (int)c));
continue;
}
}
out.append(c);
}
return out.toString();
}
public static boolean isPrintableChar(char c) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
return ! Character.isISOControl(c)
&& c != KeyEvent.CHAR_UNDEFINED
&& block != null
&& block != Character.UnicodeBlock.SPECIALS;
}
// TODO: dirty static hack. I have to refactor decoding mechanisms. // TODO: dirty static hack. I have to refactor decoding mechanisms.
public static boolean sKeepBroken = false; public static boolean sKeepBroken = false;

View File

@ -17,6 +17,7 @@
package brut.androlib.res.data.value; package brut.androlib.res.data.value;
import brut.androlib.AndrolibException; import brut.androlib.AndrolibException;
import brut.androlib.res.xml.ResXmlEncoders;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
@ -33,12 +34,12 @@ public class ResStringValue extends ResScalarValue {
@Override @Override
public String encodeAsResXmlAttr() { public String encodeAsResXmlAttr() {
return mRawValue; return ResXmlEncoders.encodeAsResXmlAttr(mRawValue);
} }
@Override @Override
public String encodeAsResXmlValue() { public String encodeAsResXmlValue() {
return mRawValue; return ResXmlEncoders.encodeAsXmlValue(mRawValue);
} }
@Override @Override

View File

@ -23,6 +23,7 @@ import java.io.Reader;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import android.util.TypedValue; import android.util.TypedValue;
import brut.androlib.AndrolibException; import brut.androlib.AndrolibException;
import brut.androlib.res.xml.ResXmlEncoders;
import brut.util.ExtDataInput; import brut.util.ExtDataInput;
import com.mindprod.ledatastream.LEDataInputStream; import com.mindprod.ledatastream.LEDataInputStream;
import java.util.logging.Level; import java.util.logging.Level;
@ -312,7 +313,8 @@ public class AXmlResourceParser implements XmlResourceParser {
if (mAttrDecoder != null) { if (mAttrDecoder != null) {
try { try {
return mAttrDecoder.decode(valueType, valueData, return mAttrDecoder.decode(valueType, valueData,
valueRaw == -1 ? null : m_strings.getString(valueRaw), valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(
m_strings.getString(valueRaw)),
getAttributeNameResource(index)); getAttributeNameResource(index));
} catch (AndrolibException ex) { } catch (AndrolibException ex) {
setFirstError(ex); setFirstError(ex);

View File

@ -17,6 +17,7 @@
package brut.androlib.res.decoder; package brut.androlib.res.decoder;
import brut.androlib.res.AndrolibResources; import brut.androlib.res.AndrolibResources;
import brut.androlib.res.xml.ResXmlEncoders;
import brut.util.ExtDataInput; import brut.util.ExtDataInput;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -126,7 +127,7 @@ public class StringBlock {
} }
int[] style = getStyle(index); int[] style = getStyle(index);
if (style == null) { if (style == null) {
return AndrolibResources.escapeTextForResXml(raw); return ResXmlEncoders.escapeXmlChars(raw);
} }
StringBuilder html = new StringBuilder(raw.length() + 32); StringBuilder html = new StringBuilder(raw.length() + 32);
int[] opened = new int[style.length / 3]; int[] opened = new int[style.length / 3];
@ -149,7 +150,7 @@ public class StringBlock {
break; break;
} }
if (offset <= end) { if (offset <= end) {
html.append(AndrolibResources.escapeCharsForResXml( html.append(ResXmlEncoders.escapeXmlChars(
raw.substring(offset, end + 1))); raw.substring(offset, end + 1)));
offset = end + 1; offset = end + 1;
} }
@ -157,7 +158,7 @@ public class StringBlock {
} }
depth = j + 1; depth = j + 1;
if (offset < start) { if (offset < start) {
html.append(AndrolibResources.escapeCharsForResXml( html.append(ResXmlEncoders.escapeXmlChars(
raw.substring(offset, start))); raw.substring(offset, start)));
offset = start; offset = start;
} }
@ -168,7 +169,7 @@ public class StringBlock {
style[i + 1] = -1; style[i + 1] = -1;
opened[depth++] = i; opened[depth++] = i;
} }
return AndrolibResources.escapeTextForResXml(html.toString(), false); return html.toString();
} }
private void outputStyleTag(String tag, StringBuilder builder, private void outputStyleTag(String tag, StringBuilder builder,
@ -199,7 +200,7 @@ public class StringBlock {
val = tag.substring(pos2 + 1); val = tag.substring(pos2 + 1);
} }
builder.append(AndrolibResources.escapeCharsForResXml(val)) builder.append(ResXmlEncoders.escapeXmlChars(val))
.append('"'); .append('"');
} }
} }

View File

@ -0,0 +1,136 @@
/**
* Copyright 2011 Ryszard Wiśniewski <brut.alll@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package brut.androlib.res.xml;
import java.awt.event.KeyEvent;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public final class ResXmlEncoders {
public static String escapeXmlChars(String str) {
return str.replace("&", "&amp;").replace("<", "&lt;");
}
public static String encodeAsResXmlAttr(String str) {
if (str.isEmpty()) {
return str;
}
char[] chars = str.toCharArray();
StringBuilder out = new StringBuilder(str.length() + 10);
switch (chars[0]) {
case '#':
case '@':
case '?':
out.append('\\');
}
for (char c : chars) {
switch (c) {
case '\\':
out.append('\\');
break;
case '"':
out.append("&quot;");
continue;
case '\n':
out.append("\\n");
continue;
default:
if (!isPrintableChar(c)) {
out.append(String.format("\\u%04x", (int) c));
continue;
}
}
out.append(c);
}
return out.toString();
}
public static String encodeAsXmlValue(String str) {
if (str.isEmpty()) {
return str;
}
char[] chars = str.toCharArray();
StringBuilder out = new StringBuilder(str.length() + 10);
switch (chars[0]) {
case '#':
case '@':
case '?':
out.append('\\');
}
boolean isInStyleTag = false;
boolean enclose = false;
boolean wasSpace = true;
for (char c : chars) {
if (isInStyleTag) {
if (c == '>') {
isInStyleTag = false;
}
} else if (c == ' ') {
if (wasSpace) {
enclose = true;
}
wasSpace = true;
} else {
wasSpace = false;
switch (c) {
case '\\':
out.append('\\');
break;
case '\'':
case '\n':
enclose = true;
break;
case '"':
out.append('\\');
break;
case '<':
isInStyleTag = true;
break;
default:
if (!isPrintableChar(c)) {
out.append(String.format("\\u%04x", (int) c));
continue;
}
}
}
out.append(c);
}
if (enclose || wasSpace) {
out.insert(0, '"').append('"');
}
return out.toString();
}
private static boolean isPrintableChar(char c) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
return !Character.isISOControl(c)
&& c != KeyEvent.CHAR_UNDEFINED
&& block != null
&& block != Character.UnicodeBlock.SPECIALS;
}
}