diff options
Diffstat (limited to 'common')
19 files changed, 1090 insertions, 0 deletions
diff --git a/common/.settings/org.eclipse.core.resources.prefs b/common/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..d8a1f3c --- /dev/null +++ b/common/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Thu Jan 05 21:13:52 PST 2012 +eclipse.preferences.version=1 +encoding//tests/src/com/android/util/PositionXmlParserTest.java=UTF-8 diff --git a/common/.settings/org.eclipse.jdt.core.prefs b/common/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..5381a0e --- /dev/null +++ b/common/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,93 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.nonnull=com.android.annotations.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.android.annotations.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled +org.eclipse.jdt.core.compiler.annotation.nullable=com.android.annotations.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/common/.settings/org.moreunit.prefs b/common/.settings/org.moreunit.prefs new file mode 100644 index 0000000..c0ed4c1 --- /dev/null +++ b/common/.settings/org.moreunit.prefs @@ -0,0 +1,5 @@ +#Thu Jan 05 10:46:32 PST 2012 +eclipse.preferences.version=1 +org.moreunit.prefixes= +org.moreunit.unitsourcefolder=common\:src\:common-tests\:src +org.moreunit.useprojectsettings=true diff --git a/common/src/com/android/io/FileWrapper.java b/common/src/com/android/io/FileWrapper.java index 2859c0d..84a1f3e 100644 --- a/common/src/com/android/io/FileWrapper.java +++ b/common/src/com/android/io/FileWrapper.java @@ -85,6 +85,7 @@ public class FileWrapper extends File implements IAbstractFile { super(uri); } + @Override public InputStream getContents() throws StreamException { try { return new FileInputStream(this); @@ -93,6 +94,7 @@ public class FileWrapper extends File implements IAbstractFile { } } + @Override public void setContents(InputStream source) throws StreamException { FileOutputStream fos = null; try { @@ -116,6 +118,7 @@ public class FileWrapper extends File implements IAbstractFile { } } + @Override public OutputStream getOutputStream() throws StreamException { try { return new FileOutputStream(this); @@ -124,10 +127,12 @@ public class FileWrapper extends File implements IAbstractFile { } } + @Override public PreferredWriteMode getPreferredWriteMode() { return PreferredWriteMode.OUTPUTSTREAM; } + @Override public String getOsLocation() { return getAbsolutePath(); } @@ -137,10 +142,12 @@ public class FileWrapper extends File implements IAbstractFile { return isFile(); } + @Override public long getModificationStamp() { return lastModified(); } + @Override public IAbstractFolder getParentFolder() { String p = this.getParent(); if (p == null) { diff --git a/common/src/com/android/io/FolderWrapper.java b/common/src/com/android/io/FolderWrapper.java index 26ed9cf..c29c934 100644 --- a/common/src/com/android/io/FolderWrapper.java +++ b/common/src/com/android/io/FolderWrapper.java @@ -81,6 +81,7 @@ public class FolderWrapper extends File implements IAbstractFolder { super(file.getAbsolutePath()); } + @Override public IAbstractResource[] listMembers() { File[] files = listFiles(); final int count = files == null ? 0 : files.length; @@ -100,8 +101,10 @@ public class FolderWrapper extends File implements IAbstractFolder { return afiles; } + @Override public boolean hasFile(final String name) { String[] match = list(new FilenameFilter() { + @Override public boolean accept(IAbstractFolder dir, String filename) { return name.equals(filename); } @@ -110,14 +113,17 @@ public class FolderWrapper extends File implements IAbstractFolder { return match.length > 0; } + @Override public IAbstractFile getFile(String name) { return new FileWrapper(this, name); } + @Override public IAbstractFolder getFolder(String name) { return new FolderWrapper(this, name); } + @Override public IAbstractFolder getParentFolder() { String p = this.getParent(); if (p == null) { @@ -126,6 +132,7 @@ public class FolderWrapper extends File implements IAbstractFolder { return new FolderWrapper(p); } + @Override public String getOsLocation() { return getAbsolutePath(); } @@ -135,6 +142,7 @@ public class FolderWrapper extends File implements IAbstractFolder { return isDirectory(); } + @Override public String[] list(FilenameFilter filter) { File[] files = listFiles(); if (files != null && files.length > 0) { diff --git a/common/src/com/android/resources/Density.java b/common/src/com/android/resources/Density.java index f838de4..610789a 100644 --- a/common/src/com/android/resources/Density.java +++ b/common/src/com/android/resources/Density.java @@ -72,6 +72,7 @@ public enum Density implements ResourceEnum { return null; } + @Override public String getResourceValue() { return mValue; } @@ -88,10 +89,12 @@ public enum Density implements ResourceEnum { return ""; } + @Override public String getShortDisplayValue() { return mDisplayValue; } + @Override public String getLongDisplayValue() { return mDisplayValue; } @@ -120,10 +123,12 @@ public enum Density implements ResourceEnum { return null; } + @Override public boolean isFakeValue() { return false; } + @Override public boolean isValidValueForDevice() { return this != NODPI; // nodpi is not a valid config for devices. } diff --git a/common/src/com/android/resources/Keyboard.java b/common/src/com/android/resources/Keyboard.java index eb99f9b..d6bc80a 100644 --- a/common/src/com/android/resources/Keyboard.java +++ b/common/src/com/android/resources/Keyboard.java @@ -53,14 +53,17 @@ public enum Keyboard implements ResourceEnum { return null; } + @Override public String getResourceValue() { return mValue; } + @Override public String getShortDisplayValue() { return mShortDisplayValue; } + @Override public String getLongDisplayValue() { return mLongDisplayValue; } @@ -89,10 +92,12 @@ public enum Keyboard implements ResourceEnum { return null; } + @Override public boolean isFakeValue() { return false; } + @Override public boolean isValidValueForDevice() { return true; } diff --git a/common/src/com/android/resources/KeyboardState.java b/common/src/com/android/resources/KeyboardState.java index e3333f5..2eb7e00 100644 --- a/common/src/com/android/resources/KeyboardState.java +++ b/common/src/com/android/resources/KeyboardState.java @@ -50,14 +50,17 @@ public enum KeyboardState implements ResourceEnum { return null; } + @Override public String getResourceValue() { return mValue; } + @Override public String getShortDisplayValue() { return mShortDisplayValue; } + @Override public String getLongDisplayValue() { return mLongDisplayValue; } @@ -86,10 +89,12 @@ public enum KeyboardState implements ResourceEnum { return null; } + @Override public boolean isFakeValue() { return false; } + @Override public boolean isValidValueForDevice() { return true; } diff --git a/common/src/com/android/resources/Navigation.java b/common/src/com/android/resources/Navigation.java index d5d9541..f857e5f 100644 --- a/common/src/com/android/resources/Navigation.java +++ b/common/src/com/android/resources/Navigation.java @@ -51,14 +51,17 @@ public enum Navigation implements ResourceEnum { return null; } + @Override public String getResourceValue() { return mValue; } + @Override public String getShortDisplayValue() { return mShortDisplayValue; } + @Override public String getLongDisplayValue() { return mLongDisplayValue; } @@ -87,10 +90,12 @@ public enum Navigation implements ResourceEnum { return null; } + @Override public boolean isFakeValue() { return false; } + @Override public boolean isValidValueForDevice() { return true; } diff --git a/common/src/com/android/resources/NavigationState.java b/common/src/com/android/resources/NavigationState.java index 266d9da..63b8fea 100644 --- a/common/src/com/android/resources/NavigationState.java +++ b/common/src/com/android/resources/NavigationState.java @@ -49,14 +49,17 @@ public enum NavigationState implements ResourceEnum { return null; } + @Override public String getResourceValue() { return mValue; } + @Override public String getShortDisplayValue() { return mShortDisplayValue; } + @Override public String getLongDisplayValue() { return mLongDisplayValue; } @@ -85,10 +88,12 @@ public enum NavigationState implements ResourceEnum { return null; } + @Override public boolean isFakeValue() { return false; } + @Override public boolean isValidValueForDevice() { return true; } diff --git a/common/src/com/android/resources/NightMode.java b/common/src/com/android/resources/NightMode.java index 2d64316..8fe1dd9 100644 --- a/common/src/com/android/resources/NightMode.java +++ b/common/src/com/android/resources/NightMode.java @@ -49,14 +49,17 @@ public enum NightMode implements ResourceEnum { return null; } + @Override public String getResourceValue() { return mValue; } + @Override public String getShortDisplayValue() { return mShortDisplayValue; } + @Override public String getLongDisplayValue() { return mLongDisplayValue; } @@ -85,10 +88,12 @@ public enum NightMode implements ResourceEnum { return null; } + @Override public boolean isFakeValue() { return false; } + @Override public boolean isValidValueForDevice() { return true; } diff --git a/common/src/com/android/resources/ScreenOrientation.java b/common/src/com/android/resources/ScreenOrientation.java index 56f907b..b18753d 100644 --- a/common/src/com/android/resources/ScreenOrientation.java +++ b/common/src/com/android/resources/ScreenOrientation.java @@ -50,14 +50,17 @@ public enum ScreenOrientation implements ResourceEnum { return null; } + @Override public String getResourceValue() { return mValue; } + @Override public String getShortDisplayValue() { return mShortDisplayValue; } + @Override public String getLongDisplayValue() { return mLongDisplayValue; } @@ -87,10 +90,12 @@ public enum ScreenOrientation implements ResourceEnum { return null; } + @Override public boolean isFakeValue() { return false; } + @Override public boolean isValidValueForDevice() { return true; } diff --git a/common/src/com/android/resources/ScreenRatio.java b/common/src/com/android/resources/ScreenRatio.java index 2794b6e..bb575b0 100644 --- a/common/src/com/android/resources/ScreenRatio.java +++ b/common/src/com/android/resources/ScreenRatio.java @@ -49,14 +49,17 @@ public enum ScreenRatio implements ResourceEnum { return null; } + @Override public String getResourceValue() { return mValue; } + @Override public String getShortDisplayValue() { return mShortDisplayValue; } + @Override public String getLongDisplayValue() { return mLongDisplayValue; } @@ -86,10 +89,12 @@ public enum ScreenRatio implements ResourceEnum { return null; } + @Override public boolean isFakeValue() { return false; } + @Override public boolean isValidValueForDevice() { return true; } diff --git a/common/src/com/android/resources/ScreenSize.java b/common/src/com/android/resources/ScreenSize.java index b6ffc50..4def540 100644 --- a/common/src/com/android/resources/ScreenSize.java +++ b/common/src/com/android/resources/ScreenSize.java @@ -51,14 +51,17 @@ public enum ScreenSize implements ResourceEnum { return null; } + @Override public String getResourceValue() { return mValue; } + @Override public String getShortDisplayValue() { return mShortDisplayValue; } + @Override public String getLongDisplayValue() { return mLongDisplayValue; } @@ -88,10 +91,12 @@ public enum ScreenSize implements ResourceEnum { return null; } + @Override public boolean isFakeValue() { return false; } + @Override public boolean isValidValueForDevice() { return true; } diff --git a/common/src/com/android/resources/TouchScreen.java b/common/src/com/android/resources/TouchScreen.java index 7ee1f0f..7eeeb08 100644 --- a/common/src/com/android/resources/TouchScreen.java +++ b/common/src/com/android/resources/TouchScreen.java @@ -50,14 +50,17 @@ public enum TouchScreen implements ResourceEnum { return null; } + @Override public String getResourceValue() { return mValue; } + @Override public String getShortDisplayValue() { return mShortDisplayValue; } + @Override public String getLongDisplayValue() { return mLongDisplayValue; } @@ -87,10 +90,12 @@ public enum TouchScreen implements ResourceEnum { return null; } + @Override public boolean isFakeValue() { return false; } + @Override public boolean isValidValueForDevice() { return true; } diff --git a/common/src/com/android/resources/UiMode.java b/common/src/com/android/resources/UiMode.java index 36c903b..d1ddbc8 100644 --- a/common/src/com/android/resources/UiMode.java +++ b/common/src/com/android/resources/UiMode.java @@ -49,14 +49,17 @@ public enum UiMode implements ResourceEnum { return null; } + @Override public String getResourceValue() { return mValue; } + @Override public String getShortDisplayValue() { return mDisplayValue; } + @Override public String getLongDisplayValue() { return mDisplayValue; } @@ -85,10 +88,12 @@ public enum UiMode implements ResourceEnum { return null; } + @Override public boolean isFakeValue() { return this == NORMAL; // NORMAL is not a real enum. it's used for internal state only. } + @Override public boolean isValidValueForDevice() { return this != NORMAL; } diff --git a/common/src/com/android/util/PositionXmlParser.java b/common/src/com/android/util/PositionXmlParser.java new file mode 100644 index 0000000..bfe8075 --- /dev/null +++ b/common/src/com/android/util/PositionXmlParser.java @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * 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 com.android.util; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.Text; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +/** + * A simple DOM XML parser which can retrieve exact beginning and end offsets + * (and line and column numbers) for element nodes as well as attribute nodes. + */ +public class PositionXmlParser { + private static final String UTF_8 = "UTF-8"; //$NON-NLS-1$ + private static final String UTF_16 = "UTF_16"; //$NON-NLS-1$ + private static final String UTF_16LE = "UTF_16LE"; //$NON-NLS-1$ + private static final String CONTENT_KEY = "contents"; //$NON-NLS-1$ + private final static String POS_KEY = "offsets"; //$NON-NLS-1$ + private static final String NAMESPACE_PREFIX_FEATURE = + "http://xml.org/sax/features/namespace-prefixes"; //$NON-NLS-1$ + private static final String NAMESPACE_FEATURE = + "http://xml.org/sax/features/namespaces"; //$NON-NLS-1$ + /** See http://www.w3.org/TR/REC-xml/#NT-EncodingDecl */ + private static final Pattern ENCODING_PATTERN = + Pattern.compile("encoding=['\"](\\S*)['\"]");//$NON-NLS-1$ + + /** + * Parses the XML content from the given input stream. + * + * @param input the input stream containing the XML to be parsed + * @return the corresponding document + * @throws ParserConfigurationException if a SAX parser is not available + * @throws SAXException if the document contains a parsing error + * @throws IOException if something is seriously wrong. This should not + * happen since the input source is known to be constructed from + * a string. + */ + @Nullable + public Document parse(@NonNull InputStream input) + throws ParserConfigurationException, SAXException, IOException { + // Read in all the data + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[1024]; + while (true) { + int r = input.read(buf); + if (r == -1) { + break; + } + out.write(buf, 0, r); + } + input.close(); + return parse(out.toByteArray()); + } + + /** + * Parses the XML content from the given byte array + * + * @param data the raw XML data (with unknown encoding) + * @return the corresponding document + * @throws ParserConfigurationException if a SAX parser is not available + * @throws SAXException if the document contains a parsing error + * @throws IOException if something is seriously wrong. This should not + * happen since the input source is known to be constructed from + * a string. + */ + @Nullable + public Document parse(@NonNull byte[] data) + throws ParserConfigurationException, SAXException, IOException { + String xml = getXmlString(data); + return parse(xml, new InputSource(new StringReader(xml)), true); + } + + /** + * Parses the given XML content. + * + * @param xml the XML string to be parsed. This must be in the correct + * encoding already. + * @return the corresponding document + * @throws ParserConfigurationException if a SAX parser is not available + * @throws SAXException if the document contains a parsing error + * @throws IOException if something is seriously wrong. This should not + * happen since the input source is known to be constructed from + * a string. + */ + @Nullable + public Document parse(@NonNull String xml) + throws ParserConfigurationException, SAXException, IOException { + return parse(xml, new InputSource(new StringReader(xml)), true); + } + + @NonNull + private Document parse(@NonNull String xml, @NonNull InputSource input, boolean checkBom) + throws ParserConfigurationException, SAXException, IOException { + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setFeature(NAMESPACE_FEATURE, true); + factory.setFeature(NAMESPACE_PREFIX_FEATURE, true); + SAXParser parser = factory.newSAXParser(); + DomBuilder handler = new DomBuilder(xml); + parser.parse(input, handler); + return handler.getDocument(); + } catch (SAXException e) { + if (checkBom && e.getMessage().contains("Content is not allowed in prolog")) { + // Byte order mark in the string? Skip it. There are many markers + // (see http://en.wikipedia.org/wiki/Byte_order_mark) so here we'll + // just skip those up to the XML prolog beginning character, < + xml = xml.replaceFirst("^([\\W]+)<","<"); //$NON-NLS-1$ //$NON-NLS-2$ + return parse(xml, null, false); + } + throw e; + } + } + + /** + * Returns the String corresponding to the given byte array of XML data + * (with unknown encoding). This method attempts to guess the encoding based + * on the XML prologue. + * @param data the XML data to be decoded into a string + * @return a string corresponding to the XML data + */ + public static String getXmlString(byte[] data) { + int offset = 0; + + String defaultCharset = UTF_8; + String charset = null; + // Look for the byte order mark, to see if we need to remove bytes from + // the input stream (and to determine whether files are big endian or little endian) etc + // for files which do not specify the encoding. + // See http://unicode.org/faq/utf_bom.html#BOM for more. + if (data.length > 4) { + if (data[0] == (byte)0xef && data[1] == (byte)0xbb && data[2] == (byte)0xbf) { + // UTF-8 + defaultCharset = charset = UTF_8; + offset += 3; + } else if (data[0] == (byte)0xfe && data[1] == (byte)0xff) { + // UTF-16, big-endian + defaultCharset = charset = UTF_16; + offset += 2; + } else if (data[0] == (byte)0x0 && data[1] == (byte)0x0 + && data[2] == (byte)0xfe && data[3] == (byte)0xff) { + // UTF-32, big-endian + defaultCharset = charset = "UTF_32"; //$NON-NLS-1$ + offset += 4; + } else if (data[0] == (byte)0xff && data[1] == (byte)0xfe + && data[2] == (byte)0x0 && data[3] == (byte)0x0) { + // UTF-32, little-endian. We must check for this *before* looking for + // UTF_16LE since UTF_32LE has the same prefix! + defaultCharset = charset = "UTF_32LE"; //$NON-NLS-1$ + offset += 4; + } else if (data[0] == (byte)0xff && data[1] == (byte)0xfe) { + // UTF-16, little-endian + defaultCharset = charset = UTF_16LE; + offset += 2; + } + } + int length = data.length - offset; + + // Guess encoding by searching for an encoding= entry in the first line. + // The prologue, and the encoding names, will always be in ASCII - which means + // we don't need to worry about strange character encodings for the prologue characters. + // However, one wrinkle is that the whole file may be encoded in something like UTF-16 + // where there are two bytes per character, so we can't just look for + // ['e','n','c','o','d','i','n','g'] etc in the byte array since there could be + // multiple bytes for each character. However, since again the prologue is in ASCII, + // we can just drop the zeroes. + boolean seenOddZero = false; + boolean seenEvenZero = false; + int prologueStart = -1; + for (int lineEnd = offset; lineEnd < data.length; lineEnd++) { + if (data[lineEnd] == 0) { + if ((lineEnd - offset) % 1 == 0) { + seenEvenZero = true; + } else { + seenOddZero = true; + } + } else if (data[lineEnd] == '\n') { + break; + } else if (data[lineEnd] == '<') { + prologueStart = lineEnd; + } else if (data[lineEnd] == '>') { + // End of prologue. Quick check to see if this is a utf-8 file since that's + // common + for (int i = lineEnd - 4; i >= 0; i--) { + if ((data[i] == 'u' || data[i] == 'U') + && (data[i + 1] == 't' || data[i + 1] == 'T') + && (data[i + 2] == 'f' || data[i + 2] == 'F') + && (data[i + 3] == '-' || data[i + 3] == '_') + && (data[i + 4] == '8') + ) { + charset = UTF_8; + break; + } + } + + if (charset == null) { + StringBuilder sb = new StringBuilder(); + for (int i = prologueStart; i <= lineEnd; i++) { + if (data[i] != 0) { + sb.append((char) data[i]); + } + } + String prologue = sb.toString(); + int encodingIndex = prologue.indexOf("encoding"); //$NON-NLS-1$ + if (encodingIndex != -1) { + Matcher matcher = ENCODING_PATTERN.matcher(prologue); + if (matcher.find(encodingIndex)) { + charset = matcher.group(1); + } + } + } + + break; + } + } + + // No prologue on the first line, and no byte order mark: Assume UTF-8/16 + if (charset == null) { + charset = seenOddZero ? UTF_16 : seenEvenZero ? UTF_16LE : UTF_8; + } + + String xml = null; + try { + xml = new String(data, offset, length, charset); + } catch (UnsupportedEncodingException e) { + try { + if (charset != defaultCharset) { + xml = new String(data, offset, length, defaultCharset); + } + } catch (UnsupportedEncodingException u) { + // Just use the default encoding below + } + } + if (xml == null) { + xml = new String(data, offset, length); + } + return xml; + } + + /** + * Returns the position for the given node. This is the start position. The + * end position can be obtained via {@link Position#getEnd()}. + * + * @param node the node to look up position for + * @return the position, or null if the node type is not supported for + * position info + */ + @Nullable + public Position getPosition(@NonNull Node node) { + // Look up the position information stored while parsing for the given node. + // Note however that we only store position information for elements (because + // there is no SAX callback for individual attributes). + // Therefore, this method special cases this: + // -- First, it looks at the owner element and uses its position + // information as a first approximation. + // -- Second, it uses that, as well as the original XML text, to search + // within the node range for an exact text match on the attribute name + // and if found uses that as the exact node offsets instead. + if (node instanceof Attr) { + Attr attr = (Attr) node; + Position pos = (Position) attr.getOwnerElement().getUserData(POS_KEY); + if (pos != null) { + int startOffset = pos.getOffset(); + int endOffset = pos.getEnd().getOffset(); + + // Find attribute in the text + String contents = (String) node.getOwnerDocument().getUserData(CONTENT_KEY); + if (contents == null) { + return null; + } + + // Locate the name=value attribute in the source text + // Fast string check first for the common occurrence + String name = attr.getName(); + Pattern pattern = Pattern.compile( + String.format("%1$s\\s*=\\s*[\"'].*[\"']", name)); //$NON-NLS-1$ + Matcher matcher = pattern.matcher(contents); + if (matcher.find(startOffset) && matcher.start() <= endOffset) { + int index = matcher.start(); + // Adjust the line and column to this new offset + int line = pos.getLine(); + int column = pos.getColumn(); + for (int offset = pos.getOffset(); offset < index; offset++) { + char t = contents.charAt(offset); + if (t == '\n') { + line++; + column = 0; + } else { + column++; + } + } + + Position attributePosition = createPosition(line, column, index); + // Also set end range for retrieval in getLocation + attributePosition.setEnd(createPosition(line, column, matcher.end())); + return attributePosition; + } else { + // No regexp match either: just fall back to element position + return pos; + } + } + } else if (node instanceof Text) { + // Position of parent element, if any + Position pos = null; + if (node.getPreviousSibling() != null) { + pos = (Position) node.getPreviousSibling().getUserData(POS_KEY); + } + if (pos == null) { + pos = (Position) node.getParentNode().getUserData(POS_KEY); + } + if (pos != null) { + // Attempt to point forward to the actual text node + int startOffset = pos.getOffset(); + int endOffset = pos.getEnd().getOffset(); + int line = pos.getLine(); + int column = pos.getColumn(); + + // Find attribute in the text + String contents = (String) node.getOwnerDocument().getUserData(CONTENT_KEY); + if (contents == null || contents.length() < endOffset) { + return null; + } + + boolean inAttribute = false; + for (int offset = startOffset; offset <= endOffset; offset++) { + char c = contents.charAt(offset); + if (c == '>' && !inAttribute) { + // Found the end of the element open tag: this is where the + // text begins. + + // Skip > + offset++; + + // Skip text whitespace prefix, if the text node contains non-whitespace + // characters + String text = node.getNodeValue(); + int textIndex = 0; + int textLength = text.length(); + int newLine = line; + int newColumn = column; + for (; textIndex < text.length(); textIndex++) { + char t = text.charAt(textIndex); + if (t == '\n') { + newLine++; + newColumn = 0; + } else { + newColumn++; + } + if (!Character.isWhitespace(t)) { + break; + } + } + if (textIndex == textLength) { + textIndex = 0; // Whitespace node + } else { + line = newLine; + column = newColumn; + } + + Position attributePosition = createPosition(line, column, + offset); + // Also set end range for retrieval in getLocation + attributePosition.setEnd(createPosition(line, column, + offset + textLength)); + return attributePosition; + } else if (c == '"') { + inAttribute = !inAttribute; + } else if (c == '\n') { + line++; + column = -1; // pre-subtract column added below + } + column++; + } + + return pos; + } + } + + return (Position) node.getUserData(POS_KEY); + } + + /** + * SAX parser handler which incrementally builds up a DOM document as we go + * along, and updates position information along the way. Position + * information is attached to the DOM nodes by setting user data with the + * {@link POS_KEY} key. + */ + private final class DomBuilder extends DefaultHandler { + private final String mXml; + private final Document mDocument; + private Locator mLocator; + private int mCurrentLine = 0; + private int mCurrentOffset; + private int mCurrentColumn; + private final List<Element> mStack = new ArrayList<Element>(); + private final StringBuilder mPendingText = new StringBuilder(); + + private DomBuilder(String xml) throws ParserConfigurationException { + mXml = xml; + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setValidating(false); + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + mDocument = docBuilder.newDocument(); + mDocument.setUserData(CONTENT_KEY, xml, null); + } + + /** Returns the document parsed by the handler */ + Document getDocument() { + return mDocument; + } + + @Override + public void setDocumentLocator(Locator locator) { + this.mLocator = locator; + } + + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + flushText(); + Element element = mDocument.createElement(qName); + for (int i = 0; i < attributes.getLength(); i++) { + if (attributes.getURI(i) != null && attributes.getURI(i).length() > 0) { + Attr attr = mDocument.createAttributeNS(attributes.getURI(i), + attributes.getQName(i)); + attr.setValue(attributes.getValue(i)); + element.setAttributeNodeNS(attr); + assert attr.getOwnerElement() == element; + } else { + Attr attr = mDocument.createAttribute(attributes.getQName(i)); + attr.setValue(attributes.getValue(i)); + element.setAttributeNode(attr); + assert attr.getOwnerElement() == element; + } + } + + Position pos = getCurrentPosition(); + + // The starting position reported to us by SAX is really the END of the + // open tag in an element, when all the attributes have been processed. + // We have to scan backwards to find the real beginning. We'll do that + // by scanning backwards. + // -1: Make sure that when we have <foo></foo> we don't consider </foo> + // the beginning since pos.offset will typically point to the first character + // AFTER the element open tag, which could be a closing tag or a child open + // tag + + for (int offset = pos.getOffset() - 1; offset >= 0; offset--) { + char c = mXml.charAt(offset); + // < cannot appear in attribute values or anywhere else within + // an element open tag, so we know the first occurrence is the real + // element start + if (c == '<') { + // Adjust line position + int line = pos.getLine(); + for (int i = offset, n = pos.getOffset(); i < n; i++) { + if (mXml.charAt(i) == '\n') { + line--; + } + } + + // Compute new column position + int column = 0; + for (int i = offset; i >= 0; i--, column++) { + if (mXml.charAt(i) == '\n') { + break; + } + } + + pos = createPosition(line, column, offset); + break; + } + } + + element.setUserData(POS_KEY, pos, null); + mStack.add(element); + } + + @Override + public void endElement(String uri, String localName, String qName) { + flushText(); + Element element = mStack.remove(mStack.size() - 1); + + Position pos = (Position) element.getUserData(POS_KEY); + assert pos != null; + pos.setEnd(getCurrentPosition()); + + if (mStack.isEmpty()) { + mDocument.appendChild(element); + } else { + Element parent = mStack.get(mStack.size() - 1); + parent.appendChild(element); + } + } + + /** + * Returns a position holder for the current position. The most + * important part of this function is to incrementally compute the + * offset as well, by counting forwards until it reaches the new line + * number and column position of the XML parser, counting characters as + * it goes along. + */ + private Position getCurrentPosition() { + int line = mLocator.getLineNumber() - 1; + int column = mLocator.getColumnNumber() - 1; + + // Compute offset incrementally now that we have the new line and column + // numbers + while (mCurrentLine < line) { + char c = mXml.charAt(mCurrentOffset); + if (c == '\r' && mCurrentOffset < mXml.length() - 1) { + if (mXml.charAt(mCurrentOffset + 1) != '\n') { + mCurrentLine++; + mCurrentColumn = 0; + } + } else if (c == '\n') { + mCurrentLine++; + mCurrentColumn = 0; + } else { + mCurrentColumn++; + } + mCurrentOffset++; + } + + mCurrentOffset += column - mCurrentColumn; + mCurrentColumn = column; + + return createPosition(mCurrentLine, mCurrentColumn, mCurrentOffset); + } + + @Override + public void characters(char c[], int start, int length) throws SAXException { + mPendingText.append(c, start, length); + } + + private void flushText() { + if (mPendingText.length() > 0 && !mStack.isEmpty()) { + Element element = mStack.get(mStack.size() - 1); + Node textNode = mDocument.createTextNode(mPendingText.toString()); + element.appendChild(textNode); + mPendingText.setLength(0); + } + } + } + + /** + * Creates a position while constructing the DOM document. This method + * allows a subclass to create a custom implementation of the position + * class. + * + * @param line the line number for the position + * @param column the column number for the position + * @param offset the character offset + * @return a new position + */ + @NonNull + protected Position createPosition(int line, int column, int offset) { + return new DefaultPosition(line, column, offset); + } + + protected interface Position { + /** + * Linked position: for a begin position this will point to the + * corresponding end position. For an end position this will be null. + * + * @return the end position, or null + */ + @Nullable + public Position getEnd(); + + /** + * Linked position: for a begin position this will point to the + * corresponding end position. For an end position this will be null. + * + * @param end the end position + */ + public void setEnd(@NonNull Position end); + + /** @return the line number, 0-based */ + public int getLine(); + + /** @return the offset number, 0-based */ + public int getOffset(); + + /** @return the column number, 0-based, and -1 if the column number if not known */ + public int getColumn(); + } + + protected static class DefaultPosition implements Position { + /** The line number (0-based where the first line is line 0) */ + private final int mLine; + private final int mColumn; + private final int mOffset; + private Position mEnd; + + /** + * Creates a new {@link Position} + * + * @param line the 0-based line number, or -1 if unknown + * @param column the 0-based column number, or -1 if unknown + * @param offset the offset, or -1 if unknown + */ + public DefaultPosition(int line, int column, int offset) { + this.mLine = line; + this.mColumn = column; + this.mOffset = offset; + } + + @Override + public int getLine() { + return mLine; + } + + @Override + public int getOffset() { + return mOffset; + } + + @Override + public int getColumn() { + return mColumn; + } + + @Override + public Position getEnd() { + return mEnd; + } + + @Override + public void setEnd(Position end) { + mEnd = end; + } + } +} diff --git a/common/tests/.classpath b/common/tests/.classpath index b793adc..15b6472 100644 --- a/common/tests/.classpath +++ b/common/tests/.classpath @@ -4,5 +4,6 @@ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> <classpathentry combineaccessrules="false" kind="src" path="/common"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/guava-tools/guava-10.0.1.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/guava-tools/src.zip"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/common/tests/src/com/android/util/PositionXmlParserTest.java b/common/tests/src/com/android/util/PositionXmlParserTest.java new file mode 100644 index 0000000..9f87252 --- /dev/null +++ b/common/tests/src/com/android/util/PositionXmlParserTest.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * 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 com.android.util; + +import com.android.util.PositionXmlParser.Position; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import java.io.BufferedOutputStream; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class PositionXmlParserTest extends TestCase { + public void test() throws Exception { + String xml = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:orientation=\"vertical\" >\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button2\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + "</LinearLayout>\n"; + PositionXmlParser parser = new PositionXmlParser(); + File file = File.createTempFile("parsertest", ".xml"); + Writer fw = new BufferedWriter(new FileWriter(file)); + fw.write(xml); + fw.close(); + Document document = parser.parse(new FileInputStream(file)); + assertNotNull(document); + + // Basic parsing heart beat tests + Element linearLayout = (Element) document.getElementsByTagName("LinearLayout").item(0); + assertNotNull(linearLayout); + NodeList buttons = document.getElementsByTagName("Button"); + assertEquals(2, buttons.getLength()); + final String ANDROID_URI = "http://schemas.android.com/apk/res/android"; + assertEquals("wrap_content", + linearLayout.getAttributeNS(ANDROID_URI, "layout_height")); + + // Check attribute positions + Attr attr = linearLayout.getAttributeNodeNS(ANDROID_URI, "layout_width"); + assertNotNull(attr); + Position start = parser.getPosition(attr); + Position end = start.getEnd(); + assertEquals(2, start.getLine()); + assertEquals(xml.indexOf("android:layout_width"), start.getOffset()); + assertEquals(2, end.getLine()); + String target = "android:layout_width=\"match_parent\""; + assertEquals(xml.indexOf(target) + target.length(), end.getOffset()); + + // Check element positions + Element button = (Element) buttons.item(0); + start = parser.getPosition(button); + end = start.getEnd(); + assertNull(end.getEnd()); + assertEquals(6, start.getLine()); + assertEquals(xml.indexOf("<Button"), start.getOffset()); + assertEquals(xml.indexOf("/>") + 2, end.getOffset()); + assertEquals(10, end.getLine()); + int button1End = end.getOffset(); + + Element button2 = (Element) buttons.item(1); + start = parser.getPosition(button2); + end = start.getEnd(); + assertEquals(12, start.getLine()); + assertEquals(xml.indexOf("<Button", button1End), start.getOffset()); + assertEquals(xml.indexOf("/>", start.getOffset()) + 2, end.getOffset()); + assertEquals(16, end.getLine()); + + file.delete(); + } + + public void testLineEndings() throws Exception { + // Test for http://code.google.com/p/android/issues/detail?id=22925 + String xml = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + + "<LinearLayout>\r\n" + + "\r" + + "<LinearLayout></LinearLayout>\r\n" + + "</LinearLayout>\r\n"; + PositionXmlParser parser = new PositionXmlParser(); + File file = File.createTempFile("parsertest2", ".xml"); + Writer fw = new BufferedWriter(new FileWriter(file)); + fw.write(xml); + fw.close(); + Document document = parser.parse(new FileInputStream(file)); + assertNotNull(document); + + file.delete(); + } + + private static void checkEncoding(String encoding, boolean writeBom, boolean writeEncoding) + throws Exception { + String value = "¾¿Œ"; + StringBuilder sb = new StringBuilder(); + + sb.append("<?xml version=\"1.0\""); + if (writeEncoding) { + sb.append(" encoding=\""); + sb.append(encoding); + sb.append("\""); + } + sb.append("?>\n" + + "<!-- This is a \n" + + " multiline comment\n" + + "-->\n" + + "<foo "); + int startAttrOffset = sb.length(); + sb.append("attr=\""); + sb.append(value); + sb.append("\""); + sb.append(">\n" + + "\n" + + "<bar></bar>\n" + + "</foo>\n"); + PositionXmlParser parser = new PositionXmlParser(); + File file = File.createTempFile("parsertest" + encoding + writeBom + writeEncoding, + ".xml"); + BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file)); + OutputStreamWriter writer = new OutputStreamWriter(stream, encoding); + + if (writeBom) { + String normalized = encoding.toLowerCase().replace("-", "_"); + if (normalized.equals("utf_8")) { + stream.write(0xef); + stream.write(0xbb); + stream.write(0xbf); + } else if (normalized.equals("utf_16")) { + stream.write(0xfe); + stream.write(0xff); + } else if (normalized.equals("utf_16le")) { + stream.write(0xff); + stream.write(0xfe); + } else if (normalized.equals("utf_32")) { + stream.write(0x0); + stream.write(0x0); + stream.write(0xfe); + stream.write(0xff); + } else if (normalized.equals("utf_32le")) { + stream.write(0xff); + stream.write(0xfe); + stream.write(0x0); + stream.write(0x0); + } else { + fail("Can't write BOM for encoding " + encoding); + } + } + + writer.write(sb.toString()); + writer.close(); + + Document document = parser.parse(new FileInputStream(file)); + assertNotNull(document); + Element root = document.getDocumentElement(); + assertEquals(file.getPath(), value, root.getAttribute("attr")); + assertEquals(4, parser.getPosition(root).getLine()); + + Attr attribute = root.getAttributeNode("attr"); + assertNotNull(attribute); + Position position = parser.getPosition(attribute); + assertNotNull(position); + assertEquals(4, position.getLine()); + assertEquals(startAttrOffset, position.getOffset()); + + file.delete(); + } + + public void testEncoding() throws Exception { + checkEncoding("utf-8", false /*bom*/, true /*encoding*/); + checkEncoding("UTF-8", false /*bom*/, true /*encoding*/); + checkEncoding("UTF_16", false /*bom*/, true /*encoding*/); + checkEncoding("UTF-16", false /*bom*/, true /*encoding*/); + checkEncoding("UTF_16LE", false /*bom*/, true /*encoding*/); + checkEncoding("UTF_32", false /*bom*/, true /*encoding*/); + checkEncoding("UTF_32LE", false /*bom*/, true /*encoding*/); + checkEncoding("windows-1252", false /*bom*/, true /*encoding*/); + checkEncoding("MacRoman", false /*bom*/, true /*encoding*/); + checkEncoding("ISO-8859-1", false /*bom*/, true /*encoding*/); + checkEncoding("iso-8859-1", false /*bom*/, true /*encoding*/); + + // Try BOM's (with no encoding specified) + checkEncoding("utf-8", true /*bom*/, false /*encoding*/); + checkEncoding("UTF-8", true /*bom*/, false /*encoding*/); + checkEncoding("UTF_16", true /*bom*/, false /*encoding*/); + checkEncoding("UTF-16", true /*bom*/, false /*encoding*/); + checkEncoding("UTF_16LE", true /*bom*/, false /*encoding*/); + checkEncoding("UTF_32", true /*bom*/, false /*encoding*/); + checkEncoding("UTF_32LE", true /*bom*/, false /*encoding*/); + + // Try default encodings (only defined for utf-8 and utf-16) + checkEncoding("utf-8", false /*bom*/, false /*encoding*/); + checkEncoding("UTF-8", false /*bom*/, false /*encoding*/); + checkEncoding("UTF_16", false /*bom*/, false /*encoding*/); + checkEncoding("UTF-16", false /*bom*/, false /*encoding*/); + checkEncoding("UTF_16LE", false /*bom*/, false /*encoding*/); + + // Try BOM's (with explicit encoding specified) + checkEncoding("utf-8", true /*bom*/, true /*encoding*/); + checkEncoding("UTF-8", true /*bom*/, true /*encoding*/); + checkEncoding("UTF_16", true /*bom*/, true /*encoding*/); + checkEncoding("UTF-16", true /*bom*/, true /*encoding*/); + checkEncoding("UTF_16LE", true /*bom*/, true /*encoding*/); + checkEncoding("UTF_32", true /*bom*/, true /*encoding*/); + checkEncoding("UTF_32LE", true /*bom*/, true /*encoding*/); + } +} |