From 85e4a1a9dd133abb879ec211ce8dd385004edf22 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Tue, 7 Aug 2012 16:30:38 -0700 Subject: Refactor common.jar Move resources and com.android.util.Pair into layoutlib_api where they belong since layoutlib depends on them and we need to control the API. Made a copy of Pair to stay in common.jar but moved it to com.android.utils.Pair (the one in com.android.util.Pair is marked as deprecated to prevent usage where applicable). Also moved XmlUtil and PositionXmlParser to com.android.utils to match Pair. Change-Id: I21d7057d3f2ce604f86a3bb1fa3c130948c93b89 --- common/src/com/android/resources/Density.java | 142 ---- .../android/resources/FolderTypeRelationship.java | 166 ----- common/src/com/android/resources/Keyboard.java | 104 --- .../src/com/android/resources/KeyboardState.java | 102 --- common/src/com/android/resources/Navigation.java | 103 --- .../src/com/android/resources/NavigationState.java | 101 --- common/src/com/android/resources/NightMode.java | 101 --- common/src/com/android/resources/ResourceEnum.java | 60 -- .../com/android/resources/ResourceFolderType.java | 78 --- common/src/com/android/resources/ResourceType.java | 111 ---- .../com/android/resources/ScreenOrientation.java | 103 --- common/src/com/android/resources/ScreenRatio.java | 103 --- common/src/com/android/resources/ScreenSize.java | 104 --- common/src/com/android/resources/TouchScreen.java | 103 --- common/src/com/android/resources/UiMode.java | 100 --- common/src/com/android/util/Pair.java | 107 --- common/src/com/android/util/PositionXmlParser.java | 729 --------------------- common/src/com/android/util/XmlUtils.java | 253 ------- common/src/com/android/utils/Pair.java | 107 +++ .../src/com/android/utils/PositionXmlParser.java | 729 +++++++++++++++++++++ common/src/com/android/utils/XmlUtils.java | 253 +++++++ 21 files changed, 1089 insertions(+), 2670 deletions(-) delete mode 100644 common/src/com/android/resources/Density.java delete mode 100644 common/src/com/android/resources/FolderTypeRelationship.java delete mode 100644 common/src/com/android/resources/Keyboard.java delete mode 100644 common/src/com/android/resources/KeyboardState.java delete mode 100644 common/src/com/android/resources/Navigation.java delete mode 100644 common/src/com/android/resources/NavigationState.java delete mode 100644 common/src/com/android/resources/NightMode.java delete mode 100644 common/src/com/android/resources/ResourceEnum.java delete mode 100644 common/src/com/android/resources/ResourceFolderType.java delete mode 100644 common/src/com/android/resources/ResourceType.java delete mode 100644 common/src/com/android/resources/ScreenOrientation.java delete mode 100644 common/src/com/android/resources/ScreenRatio.java delete mode 100644 common/src/com/android/resources/ScreenSize.java delete mode 100644 common/src/com/android/resources/TouchScreen.java delete mode 100644 common/src/com/android/resources/UiMode.java delete mode 100644 common/src/com/android/util/Pair.java delete mode 100644 common/src/com/android/util/PositionXmlParser.java delete mode 100644 common/src/com/android/util/XmlUtils.java create mode 100644 common/src/com/android/utils/Pair.java create mode 100644 common/src/com/android/utils/PositionXmlParser.java create mode 100644 common/src/com/android/utils/XmlUtils.java (limited to 'common/src/com') diff --git a/common/src/com/android/resources/Density.java b/common/src/com/android/resources/Density.java deleted file mode 100644 index 1f3fb52..0000000 --- a/common/src/com/android/resources/Density.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - - -/** - * Density enum. - *

This is used in the manifest in the uses-configuration node and in the resource folder names - * as well as other places needing to know the density values. - */ -public enum Density implements ResourceEnum { - XXHIGH("xxhdpi", "XX-High Density", 480, 16), //$NON-NLS-1$ - XHIGH("xhdpi", "X-High Density", 320, 8), //$NON-NLS-1$ - HIGH("hdpi", "High Density", 240, 4), //$NON-NLS-1$ - TV("tvdpi", "TV Density", 213, 13), //$NON-NLS-1$ - MEDIUM("mdpi", "Medium Density", 160, 4), //$NON-NLS-1$ - LOW("ldpi", "Low Density", 120, 4), //$NON-NLS-1$ - NODPI("nodpi", "No Density", 0, 4); //$NON-NLS-1$ - - public final static int DEFAULT_DENSITY = 160; - - private final String mValue; - private final String mDisplayValue; - private final int mDensity; - private final int mSince; - - private Density(String value, String displayValue, int density, int since) { - mValue = value; - mDisplayValue = displayValue; - mDensity = density; - mSince = since; - } - - /** - * Returns the enum matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no match was found. - */ - public static Density getEnum(String value) { - for (Density orient : values()) { - if (orient.mValue.equals(value)) { - return orient; - } - } - - return null; - } - - /** - * Returns the enum matching the given density value - * @param value The density value. - * @return the enum for the density value or null if no match was found. - */ - public static Density getEnum(int value) { - for (Density d : values()) { - if (d.mDensity == value) { - return d; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - public int getDpiValue() { - return mDensity; - } - - public int since() { - return mSince; - } - - public String getLegacyValue() { - if (this != NODPI) { - return String.format("%1$ddpi", getDpiValue()); - } - - return ""; - } - - @Override - public String getShortDisplayValue() { - return mDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mDisplayValue; - } - - public static int getIndex(Density value) { - int i = 0; - for (Density input : values()) { - if (value == input) { - return i; - } - - i++; - } - - return -1; - } - - public static Density getByIndex(int index) { - int i = 0; - for (Density value : values()) { - if (i == index) { - return value; - } - i++; - } - 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/FolderTypeRelationship.java b/common/src/com/android/resources/FolderTypeRelationship.java deleted file mode 100644 index 61a6d85..0000000 --- a/common/src/com/android/resources/FolderTypeRelationship.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2007 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.resources; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * This class gives access to the bidirectional relationship between {@link ResourceType} and - * {@link ResourceFolderType}. - */ -public final class FolderTypeRelationship { - - private final static Map> mTypeToFolderMap = - new HashMap>(); - - private final static Map> mFolderToTypeMap = - new HashMap>(); - - static { - // generate the relationships in a temporary map - add(ResourceType.ANIM, ResourceFolderType.ANIM); - add(ResourceType.ANIMATOR, ResourceFolderType.ANIMATOR); - add(ResourceType.ARRAY, ResourceFolderType.VALUES); - add(ResourceType.ATTR, ResourceFolderType.VALUES); - add(ResourceType.BOOL, ResourceFolderType.VALUES); - add(ResourceType.COLOR, ResourceFolderType.VALUES); - add(ResourceType.COLOR, ResourceFolderType.COLOR); - add(ResourceType.DECLARE_STYLEABLE, ResourceFolderType.VALUES); - add(ResourceType.DIMEN, ResourceFolderType.VALUES); - add(ResourceType.DRAWABLE, ResourceFolderType.VALUES); - add(ResourceType.DRAWABLE, ResourceFolderType.DRAWABLE); - add(ResourceType.FRACTION, ResourceFolderType.VALUES); - add(ResourceType.ID, ResourceFolderType.VALUES); - add(ResourceType.INTEGER, ResourceFolderType.VALUES); - add(ResourceType.INTERPOLATOR, ResourceFolderType.INTERPOLATOR); - add(ResourceType.LAYOUT, ResourceFolderType.LAYOUT); - add(ResourceType.ID, ResourceFolderType.LAYOUT); - add(ResourceType.MENU, ResourceFolderType.MENU); - add(ResourceType.ID, ResourceFolderType.MENU); - add(ResourceType.MIPMAP, ResourceFolderType.MIPMAP); - add(ResourceType.PLURALS, ResourceFolderType.VALUES); - add(ResourceType.PUBLIC, ResourceFolderType.VALUES); - add(ResourceType.RAW, ResourceFolderType.RAW); - add(ResourceType.STRING, ResourceFolderType.VALUES); - add(ResourceType.STYLE, ResourceFolderType.VALUES); - add(ResourceType.STYLEABLE, ResourceFolderType.VALUES); - add(ResourceType.XML, ResourceFolderType.XML); - - makeSafe(); - } - - /** - * Returns a list of {@link ResourceType}s that can be generated from files inside a folder - * of the specified type. - * @param folderType The folder type. - * @return a list of {@link ResourceType}, possibly empty but never null. - */ - public static List getRelatedResourceTypes(ResourceFolderType folderType) { - List list = mFolderToTypeMap.get(folderType); - if (list != null) { - return list; - } - - return Collections.emptyList(); - } - - /** - * Returns a list of {@link ResourceFolderType} that can contain files generating resources - * of the specified type. - * @param resType the type of resource. - * @return a list of {@link ResourceFolderType}, possibly empty but never null. - */ - public static List getRelatedFolders(ResourceType resType) { - List list = mTypeToFolderMap.get(resType); - if (list != null) { - return list; - } - - return Collections.emptyList(); - } - - /** - * Returns true if the {@link ResourceType} and the {@link ResourceFolderType} values match. - * @param resType the resource type. - * @param folderType the folder type. - * @return true if files inside the folder of the specified {@link ResourceFolderType} - * could generate a resource of the specified {@link ResourceType} - */ - public static boolean match(ResourceType resType, ResourceFolderType folderType) { - List list = mTypeToFolderMap.get(resType); - - if (list != null) { - return list.contains(folderType); - } - - return false; - } - - /** - * Adds a {@link ResourceType} - {@link ResourceFolderType} relationship. this indicates that - * a file in the folder can generate a resource of the specified type. - * @param type The resourceType - * @param folder The {@link ResourceFolderType} - */ - private static void add(ResourceType type, ResourceFolderType folder) { - // first we add the folder to the list associated with the type. - List folderList = mTypeToFolderMap.get(type); - if (folderList == null) { - folderList = new ArrayList(); - mTypeToFolderMap.put(type, folderList); - } - if (folderList.indexOf(folder) == -1) { - folderList.add(folder); - } - - // now we add the type to the list associated with the folder. - List typeList = mFolderToTypeMap.get(folder); - if (typeList == null) { - typeList = new ArrayList(); - mFolderToTypeMap.put(folder, typeList); - } - if (typeList.indexOf(type) == -1) { - typeList.add(type); - } - } - - /** - * Makes the maps safe by replacing the current list values with unmodifiable lists. - */ - private static void makeSafe() { - for (ResourceType type : ResourceType.values()) { - List list = mTypeToFolderMap.get(type); - if (list != null) { - // replace with a unmodifiable list wrapper around the current list. - mTypeToFolderMap.put(type, Collections.unmodifiableList(list)); - } - } - - for (ResourceFolderType folder : ResourceFolderType.values()) { - List list = mFolderToTypeMap.get(folder); - if (list != null) { - // replace with a unmodifiable list wrapper around the current list. - mFolderToTypeMap.put(folder, Collections.unmodifiableList(list)); - } - } - } -} diff --git a/common/src/com/android/resources/Keyboard.java b/common/src/com/android/resources/Keyboard.java deleted file mode 100644 index d6bc80a..0000000 --- a/common/src/com/android/resources/Keyboard.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Keyboard enum. - *

This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum Keyboard implements ResourceEnum { - NOKEY("nokeys", null, "No Keys", "No keyboard"), //$NON-NLS-1$ - QWERTY("qwerty", null, "Qwerty", "Qwerty keybard"), //$NON-NLS-1$ - TWELVEKEY("12key", "twelvekey", "12 Key", "12 key keyboard"); //$NON-NLS-1$ //$NON-NLS-2$ - - private final String mValue, mValue2; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private Keyboard(String value, String value2, String shortDisplayValue, - String longDisplayValue) { - mValue = value; - mValue2 = value2; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static Keyboard getEnum(String value) { - for (Keyboard kbrd : values()) { - if (kbrd.mValue.equals(value) || - (kbrd.mValue2 != null && kbrd.mValue2.equals(value))) { - return kbrd; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(Keyboard value) { - int i = 0; - for (Keyboard input : values()) { - if (value == input) { - return i; - } - - i++; - } - - return -1; - } - - public static Keyboard getByIndex(int index) { - int i = 0; - for (Keyboard value : values()) { - if (i == index) { - return value; - } - i++; - } - 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 deleted file mode 100644 index 2eb7e00..0000000 --- a/common/src/com/android/resources/KeyboardState.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Keyboard state enum. - *

This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum KeyboardState implements ResourceEnum { - EXPOSED("keysexposed", "Exposed", "Exposed keyboard"), //$NON-NLS-1$ - HIDDEN("keyshidden", "Hidden", "Hidden keyboard"), //$NON-NLS-1$ - SOFT("keyssoft", "Soft", "Soft keyboard"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private KeyboardState(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static KeyboardState getEnum(String value) { - for (KeyboardState state : values()) { - if (state.mValue.equals(value)) { - return state; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(KeyboardState value) { - int i = 0; - for (KeyboardState input : values()) { - if (value == input) { - return i; - } - - i++; - } - - return -1; - } - - public static KeyboardState getByIndex(int index) { - int i = 0; - for (KeyboardState value : values()) { - if (i == index) { - return value; - } - i++; - } - 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 deleted file mode 100644 index f857e5f..0000000 --- a/common/src/com/android/resources/Navigation.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Navigation enum. - *

This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum Navigation implements ResourceEnum { - NONAV("nonav", "None", "No navigation"), //$NON-NLS-1$ - DPAD("dpad", "D-pad", "D-pad navigation"), //$NON-NLS-1$ - TRACKBALL("trackball", "Trackball", "Trackball navigation"), //$NON-NLS-1$ - WHEEL("wheel", "Wheel", "Wheel navigation"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private Navigation(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static Navigation getEnum(String value) { - for (Navigation nav : values()) { - if (nav.mValue.equals(value)) { - return nav; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(Navigation value) { - int i = 0; - for (Navigation nav : values()) { - if (nav == value) { - return i; - } - - i++; - } - - return -1; - } - - public static Navigation getByIndex(int index) { - int i = 0; - for (Navigation value : values()) { - if (i == index) { - return value; - } - i++; - } - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } - -} \ No newline at end of file diff --git a/common/src/com/android/resources/NavigationState.java b/common/src/com/android/resources/NavigationState.java deleted file mode 100644 index 63b8fea..0000000 --- a/common/src/com/android/resources/NavigationState.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Navigation state enum. - *

This is used in the resource folder names. - */ -public enum NavigationState implements ResourceEnum { - EXPOSED("navexposed", "Exposed", "Exposed navigation"), //$NON-NLS-1$ - HIDDEN("navhidden", "Hidden", "Hidden navigation"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private NavigationState(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static NavigationState getEnum(String value) { - for (NavigationState state : values()) { - if (state.mValue.equals(value)) { - return state; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(NavigationState value) { - int i = 0; - for (NavigationState input : values()) { - if (value == input) { - return i; - } - - i++; - } - - return -1; - } - - public static NavigationState getByIndex(int index) { - int i = 0; - for (NavigationState value : values()) { - if (i == index) { - return value; - } - i++; - } - 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 deleted file mode 100644 index 8fe1dd9..0000000 --- a/common/src/com/android/resources/NightMode.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Night enum. - *

This is used in the resource folder names. - */ -public enum NightMode implements ResourceEnum { - NOTNIGHT("notnight", "Not Night", "Day time"), - NIGHT("night", "Night", "Night time"); - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private NightMode(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static NightMode getEnum(String value) { - for (NightMode mode : values()) { - if (mode.mValue.equals(value)) { - return mode; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(NightMode value) { - int i = 0; - for (NightMode mode : values()) { - if (mode == value) { - return i; - } - - i++; - } - - return -1; - } - - public static NightMode getByIndex(int index) { - int i = 0; - for (NightMode value : values()) { - if (i == index) { - return value; - } - i++; - } - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } - -} diff --git a/common/src/com/android/resources/ResourceEnum.java b/common/src/com/android/resources/ResourceEnum.java deleted file mode 100644 index 7f4e16a..0000000 --- a/common/src/com/android/resources/ResourceEnum.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * An enum representing a resource qualifier value. - */ -public interface ResourceEnum { - - /** - * Returns the resource string. This is to be used in resource folder names. - */ - String getResourceValue(); - - /** - * Whether the value actually used on device. This returns true only if a device can report - * this value, false if it's just used to qualify resources. - */ - boolean isValidValueForDevice(); - - /** - * Whether the value is neither used for device nor resources. This returns false when - * the value is only used for internal usage in the custom editors. - */ - boolean isFakeValue(); - - /** - * Returns a short string for display value. The string does not need to show the context. - *

For instance "exposed", which can be the value for the keyboard state or the navigation - * state, would be valid since something else in the UI is expected to show if this is about the - * keyboard or the navigation state. - * - * @see #getLongDisplayValue() - */ - String getShortDisplayValue(); - - /** - * Returns a long string for display value. This must not only include the enum value but - * context (qualifier) about what the value represents. - *

For instance "Exposed keyboard", and "Export navigation", as "exposed" would not be - * enough to know what qualifier the value is about. - * - * @see #getShortDisplayValue() - */ - String getLongDisplayValue(); -} diff --git a/common/src/com/android/resources/ResourceFolderType.java b/common/src/com/android/resources/ResourceFolderType.java deleted file mode 100644 index 3a5b64d..0000000 --- a/common/src/com/android/resources/ResourceFolderType.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2007 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.resources; - -import com.android.AndroidConstants; - -/** - * Enum representing a type of resource folder. - */ -public enum ResourceFolderType { - ANIM(AndroidConstants.FD_RES_ANIM), - ANIMATOR(AndroidConstants.FD_RES_ANIMATOR), - COLOR(AndroidConstants.FD_RES_COLOR), - DRAWABLE(AndroidConstants.FD_RES_DRAWABLE), - INTERPOLATOR(AndroidConstants.FD_RES_INTERPOLATOR), - LAYOUT(AndroidConstants.FD_RES_LAYOUT), - MENU(AndroidConstants.FD_RES_MENU), - MIPMAP(AndroidConstants.FD_RES_MIPMAP), - RAW(AndroidConstants.FD_RES_RAW), - VALUES(AndroidConstants.FD_RES_VALUES), - XML(AndroidConstants.FD_RES_XML); - - private final String mName; - - ResourceFolderType(String name) { - mName = name; - } - - /** - * Returns the folder name for this resource folder type. - */ - public String getName() { - return mName; - } - - /** - * Returns the enum by name. - * @param name The enum string value. - * @return the enum or null if not found. - */ - public static ResourceFolderType getTypeByName(String name) { - for (ResourceFolderType rType : values()) { - if (rType.mName.equals(name)) { - return rType; - } - } - return null; - } - - /** - * Returns the {@link ResourceFolderType} from the folder name - * @param folderName The name of the folder. This must be a valid folder name in the format - * resType[-resqualifiers[-resqualifiers[...]] - * @return the ResourceFolderType representing the type of the folder, or - * null if no matching type was found. - */ - public static ResourceFolderType getFolderType(String folderName) { - // split the name of the folder in segments. - String[] folderSegments = folderName.split(AndroidConstants.RES_QUALIFIER_SEP); - - // get the enum for the resource type. - return getTypeByName(folderSegments[0]); - } -} diff --git a/common/src/com/android/resources/ResourceType.java b/common/src/com/android/resources/ResourceType.java deleted file mode 100644 index e9d4d53..0000000 --- a/common/src/com/android/resources/ResourceType.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2007 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.resources; - - -/** - * Enum representing a type of compiled resource. - */ -public enum ResourceType { - ANIM("anim", "Animation"), //$NON-NLS-1$ - ANIMATOR("animator", "Animator"), //$NON-NLS-1$ - ARRAY("array", "Array", "string-array", "integer-array"), //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-4$ - ATTR("attr", "Attr"), //$NON-NLS-1$ - BOOL("bool", "Boolean"), //$NON-NLS-1$ - COLOR("color", "Color"), //$NON-NLS-1$ - DECLARE_STYLEABLE("declare-styleable", "Declare Stylable"), //$NON-NLS-1$ - DIMEN("dimen", "Dimension"), //$NON-NLS-1$ - DRAWABLE("drawable", "Drawable"), //$NON-NLS-1$ - FRACTION("fraction", "Fraction"), //$NON-NLS-1$ - ID("id", "ID"), //$NON-NLS-1$ - INTEGER("integer", "Integer"), //$NON-NLS-1$ - INTERPOLATOR("interpolator", "Interpolator"), //$NON-NLS-1$ - LAYOUT("layout", "Layout"), //$NON-NLS-1$ - MENU("menu", "Menu"), //$NON-NLS-1$ - MIPMAP("mipmap", "Mip Map"), //$NON-NLS-1$ - PLURALS("plurals", "Plurals"), //$NON-NLS-1$ - RAW("raw", "Raw"), //$NON-NLS-1$ - STRING("string", "String"), //$NON-NLS-1$ - STYLE("style", "Style"), //$NON-NLS-1$ - STYLEABLE("styleable", "Styleable"), //$NON-NLS-1$ - XML("xml", "XML"), //$NON-NLS-1$ - // this is not actually used. Only there because they get parsed and since we want to - // detect new resource type, we need to have this one exist. - PUBLIC("public", "###"); //$NON-NLS-1$ //$NON-NLS-2$ - - private final String mName; - private final String mDisplayName; - private final String[] mAlternateXmlNames; - - ResourceType(String name, String displayName, String... alternateXmlNames) { - mName = name; - mDisplayName = displayName; - mAlternateXmlNames = alternateXmlNames; - } - - /** - * Returns the resource type name, as used by XML files. - */ - public String getName() { - return mName; - } - - /** - * Returns a translated display name for the resource type. - */ - public String getDisplayName() { - return mDisplayName; - } - - /** - * Returns the enum by its name as it appears in the XML or the R class. - * @param name name of the resource - * @return the matching {@link ResourceType} or null if no match was found. - */ - public static ResourceType getEnum(String name) { - for (ResourceType rType : values()) { - if (rType.mName.equals(name)) { - return rType; - } else if (rType.mAlternateXmlNames != null) { - // if there are alternate Xml Names, we test those too - for (String alternate : rType.mAlternateXmlNames) { - if (alternate.equals(name)) { - return rType; - } - } - } - } - return null; - } - - /** - * Returns an array with all the names defined by this enum. - */ - public static String[] getNames() { - ResourceType[] values = values(); - String[] names = new String[values.length]; - for (int i = values.length - 1; i >= 0; --i) { - names[i] = values[i].getName(); - } - return names; - } - - @Override - public String toString() { - return getName(); - } -} diff --git a/common/src/com/android/resources/ScreenOrientation.java b/common/src/com/android/resources/ScreenOrientation.java deleted file mode 100644 index b18753d..0000000 --- a/common/src/com/android/resources/ScreenOrientation.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Screen Orientation enum. - *

This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum ScreenOrientation implements ResourceEnum { - PORTRAIT("port", "Portrait", "Portrait Orientation"), //$NON-NLS-1$ - LANDSCAPE("land", "Landscape", "Landscape Orientation"), //$NON-NLS-1$ - SQUARE("square", "Square", "Square Orientation"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private ScreenOrientation(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static ScreenOrientation getEnum(String value) { - for (ScreenOrientation orient : values()) { - if (orient.mValue.equals(value)) { - return orient; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(ScreenOrientation orientation) { - int i = 0; - for (ScreenOrientation orient : values()) { - if (orient == orientation) { - return i; - } - - i++; - } - - return -1; - } - - public static ScreenOrientation getByIndex(int index) { - int i = 0; - for (ScreenOrientation orient : values()) { - if (i == index) { - return orient; - } - i++; - } - - 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 deleted file mode 100644 index bb575b0..0000000 --- a/common/src/com/android/resources/ScreenRatio.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Screen Ratio enum. - *

This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum ScreenRatio implements ResourceEnum { - NOTLONG("notlong", "Not Long", "Short screen aspect ratio"), //$NON-NLS-1$ - LONG("long", "Long", "Long screen aspect ratio"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private ScreenRatio(String value, String displayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = displayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static ScreenRatio getEnum(String value) { - for (ScreenRatio orient : values()) { - if (orient.mValue.equals(value)) { - return orient; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(ScreenRatio orientation) { - int i = 0; - for (ScreenRatio orient : values()) { - if (orient == orientation) { - return i; - } - - i++; - } - - return -1; - } - - public static ScreenRatio getByIndex(int index) { - int i = 0; - for (ScreenRatio orient : values()) { - if (i == index) { - return orient; - } - i++; - } - - 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 deleted file mode 100644 index 4def540..0000000 --- a/common/src/com/android/resources/ScreenSize.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Screen size enum. - *

This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum ScreenSize implements ResourceEnum { - SMALL("small", "Small", "Small Screen"), //$NON-NLS-1$ - NORMAL("normal", "Normal", "Normal Screen"), //$NON-NLS-1$ - LARGE("large", "Large", "Large Screen"), //$NON-NLS-1$ - XLARGE("xlarge", "X-Large", "Extra Large Screen"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private ScreenSize(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static ScreenSize getEnum(String value) { - for (ScreenSize orient : values()) { - if (orient.mValue.equals(value)) { - return orient; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(ScreenSize orientation) { - int i = 0; - for (ScreenSize orient : values()) { - if (orient == orientation) { - return i; - } - - i++; - } - - return -1; - } - - public static ScreenSize getByIndex(int index) { - int i = 0; - for (ScreenSize orient : values()) { - if (i == index) { - return orient; - } - i++; - } - - 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 deleted file mode 100644 index 7eeeb08..0000000 --- a/common/src/com/android/resources/TouchScreen.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Touch screen enum. - *

This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum TouchScreen implements ResourceEnum { - NOTOUCH("notouch", "No Touch", "No-touch screen"), //$NON-NLS-1$ - STYLUS("stylus", "Stylus", "Stylus-based touchscreen"), //$NON-NLS-1$ - FINGER("finger", "Finger", "Finger-based touchscreen"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private TouchScreen(String value, String displayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = displayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static TouchScreen getEnum(String value) { - for (TouchScreen orient : values()) { - if (orient.mValue.equals(value)) { - return orient; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(TouchScreen touch) { - int i = 0; - for (TouchScreen t : values()) { - if (t == touch) { - return i; - } - - i++; - } - - return -1; - } - - public static TouchScreen getByIndex(int index) { - int i = 0; - for (TouchScreen value : values()) { - if (i == index) { - return value; - } - i++; - } - - 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 deleted file mode 100644 index d1ddbc8..0000000 --- a/common/src/com/android/resources/UiMode.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * UI Mode enum. - *

This is used in the resource folder names. - */ -public enum UiMode implements ResourceEnum { - NORMAL("", "Normal"), - CAR("car", "Car Dock"), - DESK("desk", "Desk Dock"), - TELEVISION("television", "Television"); - - private final String mValue; - private final String mDisplayValue; - - private UiMode(String value, String display) { - mValue = value; - mDisplayValue = display; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static UiMode getEnum(String value) { - for (UiMode mode : values()) { - if (mode.mValue.equals(value)) { - return mode; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mDisplayValue; - } - - public static int getIndex(UiMode value) { - int i = 0; - for (UiMode mode : values()) { - if (mode == value) { - return i; - } - - i++; - } - - return -1; - } - - public static UiMode getByIndex(int index) { - int i = 0; - for (UiMode value : values()) { - if (i == index) { - return value; - } - i++; - } - 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/Pair.java b/common/src/com/android/util/Pair.java deleted file mode 100644 index 8817cd7..0000000 --- a/common/src/com/android/util/Pair.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2010 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; - -/** - * A Pair class is simply a 2-tuple for use in this package. We might want to - * think about adding something like this to a more central utility place, or - * replace it by a common tuple class if one exists, or even rewrite the layout - * classes using this Pair by a more dedicated data structure (so we don't have - * to pass around generic signatures as is currently done, though at least the - * construction is helped a bit by the {@link #of} factory method. - * - * @param The type of the first value - * @param The type of the second value - */ -public class Pair { - private final S mFirst; - private final T mSecond; - - // Use {@link Pair#of} factory instead since it infers generic types - private Pair(S first, T second) { - this.mFirst = first; - this.mSecond = second; - } - - /** - * Return the first item in the pair - * - * @return the first item in the pair - */ - public S getFirst() { - return mFirst; - } - - /** - * Return the second item in the pair - * - * @return the second item in the pair - */ - public T getSecond() { - return mSecond; - } - - /** - * Constructs a new pair of the given two objects, inferring generic types. - * - * @param first the first item to store in the pair - * @param second the second item to store in the pair - * @param the type of the first item - * @param the type of the second item - * @return a new pair wrapping the two items - */ - public static Pair of(S first, T second) { - return new Pair(first,second); - } - - @Override - public String toString() { - return "Pair [first=" + mFirst + ", second=" + mSecond + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mFirst == null) ? 0 : mFirst.hashCode()); - result = prime * result + ((mSecond == null) ? 0 : mSecond.hashCode()); - return result; - } - - @SuppressWarnings("unchecked") - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Pair other = (Pair) obj; - if (mFirst == null) { - if (other.mFirst != null) - return false; - } else if (!mFirst.equals(other.mFirst)) - return false; - if (mSecond == null) { - if (other.mSecond != null) - return false; - } else if (!mSecond.equals(other.mSecond)) - return false; - return true; - } -} diff --git a/common/src/com/android/util/PositionXmlParser.java b/common/src/com/android/util/PositionXmlParser.java deleted file mode 100644 index fe22f58..0000000 --- a/common/src/com/android/util/PositionXmlParser.java +++ /dev/null @@ -1,729 +0,0 @@ -/* - * 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, new InputSource(new StringReader(xml)), 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' || data[lineEnd] == '\r') { - 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) { - return getPosition(node, -1, -1); - } - - /** - * Returns the position for the given node. This is the start position. The - * end position can be obtained via {@link Position#getEnd()}. A specific - * range within the node can be specified with the {@code start} and - * {@code end} parameters. - * - * @param node the node to look up position for - * @param start the relative offset within the node range to use as the - * starting position, inclusive, or -1 to not limit the range - * @param end the relative offset within the node range to use as the ending - * position, or -1 to not limit the range - * @return the position, or null if the node type is not supported for - * position info - */ - @Nullable - public Position getPosition(@NonNull Node node, int start, int end) { - // 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(); - if (start != -1) { - startOffset += start; - if (end != -1) { - endOffset = start + end; - } - } - - // 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() - index, - 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++; - column++; - - String text = node.getNodeValue(); - int textIndex = 0; - int textLength = text.length(); - int newLine = line; - int newColumn = column; - if (start != -1) { - textLength = Math.min(textLength, start); - for (; textIndex < textLength; textIndex++) { - char t = text.charAt(textIndex); - if (t == '\n') { - newLine++; - newColumn = 0; - } else { - newColumn++; - } - } - } else { - // Skip text whitespace prefix, if the text node contains - // non-whitespace characters - for (; textIndex < textLength; textIndex++) { - char t = text.charAt(textIndex); - if (t == '\n') { - newLine++; - newColumn = 0; - } else if (!Character.isWhitespace(t)) { - break; - } else { - newColumn++; - } - } - } - if (textIndex == text.length()) { - textIndex = 0; // Whitespace node - } else { - line = newLine; - column = newColumn; - } - - Position attributePosition = createPosition(line, column, - offset + textIndex); - // Also set end range for retrieval in getLocation - if (end != -1) { - attributePosition.setEnd(createPosition(line, column, - offset + end)); - } else { - 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 mStack = new ArrayList(); - 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 { - try { - 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 we don't consider - // 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 - 1; 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); - } catch (Exception t) { - throw new SAXException(t); - } - } - - @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 - int xmlLength = mXml.length(); - while (mCurrentLine < line && mCurrentOffset < xmlLength) { - char c = mXml.charAt(mCurrentOffset); - if (c == '\r' && mCurrentOffset < xmlLength - 1) { - if (mXml.charAt(mCurrentOffset + 1) != '\n') { - mCurrentLine++; - mCurrentColumn = 0; - } - } else if (c == '\n') { - mCurrentLine++; - mCurrentColumn = 0; - } else { - mCurrentColumn++; - } - mCurrentOffset++; - } - - mCurrentOffset += column - mCurrentColumn; - if (mCurrentOffset >= xmlLength) { - // The parser sometimes passes wrong column numbers at the - // end of the file: Ensure that the offset remains valid. - mCurrentOffset = xmlLength; - } - 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(@NonNull Position end) { - mEnd = end; - } - } -} diff --git a/common/src/com/android/util/XmlUtils.java b/common/src/com/android/util/XmlUtils.java deleted file mode 100644 index 11db031..0000000 --- a/common/src/com/android/util/XmlUtils.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2012 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.NamedNodeMap; -import org.w3c.dom.Node; - -import java.util.HashSet; - -/** XML Utilities */ -public class XmlUtils { - /** Namespace used in XML files for Android attributes */ - public static final String ANDROID_URI = - "http://schemas.android.com/apk/res/android"; //$NON-NLS-1$ - /** Namespace used in XML files for Android Tooling attributes */ - public static final String TOOLS_URI = - "http://schemas.android.com/tools"; //$NON-NLS-1$ - /** URI of the reserved "xmlns" prefix */ - public final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; //$NON-NLS-1$ - /** The "xmlns" attribute name */ - public static final String XMLNS = "xmlns"; //$NON-NLS-1$ - /** The default prefix used for the {@link #XMLNS_URI} */ - public static final String XMLNS_PREFIX = "xmlns:"; //$NON-NLS-1$ - /** Qualified name of the xmlns android declaration element */ - public static final String XMLNS_ANDROID = "xmlns:android"; //$NON-NLS-1$ - /** The default prefix used for the {@link #ANDROID_URI} name space */ - public static final String ANDROID_NS_NAME = "android"; //$NON-NLS-1$ - /** The default prefix used for the {@link #ANDROID_URI} name space including the colon */ - public static final String ANDROID_NS_NAME_PREFIX = "android:"; //$NON-NLS-1$ - /** The default prefix used for the app */ - private static final String APP_PREFIX = "app"; //$NON-NLS-1$ - /** The "xmlns:" attribute prefix used for namespace declarations */ - public static final String XMLNS_COLON = "xmlns:"; //$NON-NLS-1$ - /** The entity for the ampersand character */ - public static final String AMP_ENTITY = "&"; //$NON-NLS-1$ - /** The entity for the quote character */ - public static final String QUOT_ENTITY = """; //$NON-NLS-1$ - /** The entity for the apostrophe character */ - public static final String APOS_ENTITY = "'"; //$NON-NLS-1$ - /** The entity for the less than character */ - public static final String LT_ENTITY = "<"; //$NON-NLS-1$ - /** The entity for the greater than character */ - public static final String GT_ENTITY = ">"; //$NON-NLS-1$ - - /** - * Returns the namespace prefix matching the requested namespace URI. - * If no such declaration is found, returns the default "android" prefix for - * the Android URI, and "app" for other URI's. - * - * @param node The current node. Must not be null. - * @param nsUri The namespace URI of which the prefix is to be found, - * e.g. SdkConstants.NS_RESOURCES - * @return The first prefix declared or the default "android" prefix - * (or "app" for non-Android URIs) - */ - @NonNull - public static String lookupNamespacePrefix(@NonNull Node node, @NonNull String nsUri) { - String defaultPrefix = ANDROID_URI.equals(nsUri) ? ANDROID_NS_NAME : APP_PREFIX; - return lookupNamespacePrefix(node, nsUri, defaultPrefix); - } - - /** - * Returns the namespace prefix matching the requested namespace URI. - * If no such declaration is found, returns the default "android" prefix. - * - * @param node The current node. Must not be null. - * @param nsUri The namespace URI of which the prefix is to be found, - * e.g. SdkConstants.NS_RESOURCES - * @param defaultPrefix The default prefix (root) to use if the namespace - * is not found. If null, do not create a new namespace - * if this URI is not defined for the document. - * @return The first prefix declared or the provided prefix (possibly with - * a number appended to avoid conflicts with existing prefixes. - */ - public static String lookupNamespacePrefix( - @Nullable Node node, @Nullable String nsUri, @Nullable String defaultPrefix) { - // Note: Node.lookupPrefix is not implemented in wst/xml/core NodeImpl.java - // The following code emulates this simple call: - // String prefix = node.lookupPrefix(SdkConstants.NS_RESOURCES); - - // if the requested URI is null, it denotes an attribute with no namespace. - if (nsUri == null) { - return null; - } - - // per XML specification, the "xmlns" URI is reserved - if (XMLNS_URI.equals(nsUri)) { - return XMLNS; - } - - HashSet visited = new HashSet(); - Document doc = node == null ? null : node.getOwnerDocument(); - - // Ask the document about it. This method may not be implemented by the Document. - String nsPrefix = null; - try { - nsPrefix = doc != null ? doc.lookupPrefix(nsUri) : null; - if (nsPrefix != null) { - return nsPrefix; - } - } catch (Throwable t) { - // ignore - } - - // If that failed, try to look it up manually. - // This also gathers prefixed in use in the case we want to generate a new one below. - for (; node != null && node.getNodeType() == Node.ELEMENT_NODE; - node = node.getParentNode()) { - NamedNodeMap attrs = node.getAttributes(); - for (int n = attrs.getLength() - 1; n >= 0; --n) { - Node attr = attrs.item(n); - if (XMLNS.equals(attr.getPrefix())) { - String uri = attr.getNodeValue(); - nsPrefix = attr.getLocalName(); - // Is this the URI we are looking for? If yes, we found its prefix. - if (nsUri.equals(uri)) { - return nsPrefix; - } - visited.add(nsPrefix); - } - } - } - - // Failed the find a prefix. Generate a new sensible default prefix, unless - // defaultPrefix was null in which case the caller does not want the document - // modified. - if (defaultPrefix == null) { - return null; - } - - // - // We need to make sure the prefix is not one that was declared in the scope - // visited above. Pick a unique prefix from the provided default prefix. - String prefix = defaultPrefix; - String base = prefix; - for (int i = 1; visited.contains(prefix); i++) { - prefix = base + Integer.toString(i); - } - // Also create & define this prefix/URI in the XML document as an attribute in the - // first element of the document. - if (doc != null) { - node = doc.getFirstChild(); - while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { - node = node.getNextSibling(); - } - if (node != null) { - // This doesn't work: - //Attr attr = doc.createAttributeNS(XMLNS_URI, prefix); - //attr.setPrefix(XMLNS); - // - // Xerces throws - //org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or - // change an object in a way which is incorrect with regard to namespaces. - // - // Instead pass in the concatenated prefix. (This is covered by - // the UiElementNodeTest#testCreateNameSpace() test.) - Attr attr = doc.createAttributeNS(XMLNS_URI, XMLNS_PREFIX + prefix); - attr.setValue(nsUri); - node.getAttributes().setNamedItemNS(attr); - } - } - - return prefix; - } - - /** - * Converts the given attribute value to an XML-attribute-safe value, meaning that - * single and double quotes are replaced with their corresponding XML entities. - * - * @param attrValue the value to be escaped - * @return the escaped value - */ - @NonNull - public static String toXmlAttributeValue(@NonNull String attrValue) { - for (int i = 0, n = attrValue.length(); i < n; i++) { - char c = attrValue.charAt(i); - if (c == '"' || c == '\'' || c == '<' || c == '&') { - StringBuilder sb = new StringBuilder(2 * attrValue.length()); - appendXmlAttributeValue(sb, attrValue); - return sb.toString(); - } - } - - return attrValue; - } - - /** - * Appends text to the given {@link StringBuilder} and escapes it as required for a - * DOM attribute node. - * - * @param sb the string builder - * @param attrValue the attribute value to be appended and escaped - */ - public static void appendXmlAttributeValue(@NonNull StringBuilder sb, - @NonNull String attrValue) { - int n = attrValue.length(); - // &, ", ' and < are illegal in attributes; see http://www.w3.org/TR/REC-xml/#NT-AttValue - // (' legal in a " string and " is legal in a ' string but here we'll stay on the safe - // side) - for (int i = 0; i < n; i++) { - char c = attrValue.charAt(i); - if (c == '"') { - sb.append(QUOT_ENTITY); - } else if (c == '<') { - sb.append(LT_ENTITY); - } else if (c == '\'') { - sb.append(APOS_ENTITY); - } else if (c == '&') { - sb.append(AMP_ENTITY); - } else { - sb.append(c); - } - } - } - - /** - * Appends text to the given {@link StringBuilder} and escapes it as required for a - * DOM text node. - * - * @param sb the string builder - * @param textValue the text value to be appended and escaped - */ - public static void appendXmlTextValue(@NonNull StringBuilder sb, @NonNull String textValue) { - for (int i = 0, n = textValue.length(); i < n; i++) { - char c = textValue.charAt(i); - if (c == '<') { - sb.append(LT_ENTITY); - } else if (c == '&') { - sb.append(AMP_ENTITY); - } else { - sb.append(c); - } - } - } -} diff --git a/common/src/com/android/utils/Pair.java b/common/src/com/android/utils/Pair.java new file mode 100644 index 0000000..63694de --- /dev/null +++ b/common/src/com/android/utils/Pair.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010 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.utils; + +/** + * A Pair class is simply a 2-tuple for use in this package. We might want to + * think about adding something like this to a more central utility place, or + * replace it by a common tuple class if one exists, or even rewrite the layout + * classes using this Pair by a more dedicated data structure (so we don't have + * to pass around generic signatures as is currently done, though at least the + * construction is helped a bit by the {@link #of} factory method. + * + * @param The type of the first value + * @param The type of the second value + */ +public class Pair { + private final S mFirst; + private final T mSecond; + + // Use {@link Pair#of} factory instead since it infers generic types + private Pair(S first, T second) { + this.mFirst = first; + this.mSecond = second; + } + + /** + * Return the first item in the pair + * + * @return the first item in the pair + */ + public S getFirst() { + return mFirst; + } + + /** + * Return the second item in the pair + * + * @return the second item in the pair + */ + public T getSecond() { + return mSecond; + } + + /** + * Constructs a new pair of the given two objects, inferring generic types. + * + * @param first the first item to store in the pair + * @param second the second item to store in the pair + * @param the type of the first item + * @param the type of the second item + * @return a new pair wrapping the two items + */ + public static Pair of(S first, T second) { + return new Pair(first,second); + } + + @Override + public String toString() { + return "Pair [first=" + mFirst + ", second=" + mSecond + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((mFirst == null) ? 0 : mFirst.hashCode()); + result = prime * result + ((mSecond == null) ? 0 : mSecond.hashCode()); + return result; + } + + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Pair other = (Pair) obj; + if (mFirst == null) { + if (other.mFirst != null) + return false; + } else if (!mFirst.equals(other.mFirst)) + return false; + if (mSecond == null) { + if (other.mSecond != null) + return false; + } else if (!mSecond.equals(other.mSecond)) + return false; + return true; + } +} diff --git a/common/src/com/android/utils/PositionXmlParser.java b/common/src/com/android/utils/PositionXmlParser.java new file mode 100644 index 0000000..1eae641 --- /dev/null +++ b/common/src/com/android/utils/PositionXmlParser.java @@ -0,0 +1,729 @@ +/* + * 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.utils; + +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, new InputSource(new StringReader(xml)), 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' || data[lineEnd] == '\r') { + 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) { + return getPosition(node, -1, -1); + } + + /** + * Returns the position for the given node. This is the start position. The + * end position can be obtained via {@link Position#getEnd()}. A specific + * range within the node can be specified with the {@code start} and + * {@code end} parameters. + * + * @param node the node to look up position for + * @param start the relative offset within the node range to use as the + * starting position, inclusive, or -1 to not limit the range + * @param end the relative offset within the node range to use as the ending + * position, or -1 to not limit the range + * @return the position, or null if the node type is not supported for + * position info + */ + @Nullable + public Position getPosition(@NonNull Node node, int start, int end) { + // 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(); + if (start != -1) { + startOffset += start; + if (end != -1) { + endOffset = start + end; + } + } + + // 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() - index, + 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++; + column++; + + String text = node.getNodeValue(); + int textIndex = 0; + int textLength = text.length(); + int newLine = line; + int newColumn = column; + if (start != -1) { + textLength = Math.min(textLength, start); + for (; textIndex < textLength; textIndex++) { + char t = text.charAt(textIndex); + if (t == '\n') { + newLine++; + newColumn = 0; + } else { + newColumn++; + } + } + } else { + // Skip text whitespace prefix, if the text node contains + // non-whitespace characters + for (; textIndex < textLength; textIndex++) { + char t = text.charAt(textIndex); + if (t == '\n') { + newLine++; + newColumn = 0; + } else if (!Character.isWhitespace(t)) { + break; + } else { + newColumn++; + } + } + } + if (textIndex == text.length()) { + textIndex = 0; // Whitespace node + } else { + line = newLine; + column = newColumn; + } + + Position attributePosition = createPosition(line, column, + offset + textIndex); + // Also set end range for retrieval in getLocation + if (end != -1) { + attributePosition.setEnd(createPosition(line, column, + offset + end)); + } else { + 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 mStack = new ArrayList(); + 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 { + try { + 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 we don't consider + // 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 - 1; 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); + } catch (Exception t) { + throw new SAXException(t); + } + } + + @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 + int xmlLength = mXml.length(); + while (mCurrentLine < line && mCurrentOffset < xmlLength) { + char c = mXml.charAt(mCurrentOffset); + if (c == '\r' && mCurrentOffset < xmlLength - 1) { + if (mXml.charAt(mCurrentOffset + 1) != '\n') { + mCurrentLine++; + mCurrentColumn = 0; + } + } else if (c == '\n') { + mCurrentLine++; + mCurrentColumn = 0; + } else { + mCurrentColumn++; + } + mCurrentOffset++; + } + + mCurrentOffset += column - mCurrentColumn; + if (mCurrentOffset >= xmlLength) { + // The parser sometimes passes wrong column numbers at the + // end of the file: Ensure that the offset remains valid. + mCurrentOffset = xmlLength; + } + 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(@NonNull Position end) { + mEnd = end; + } + } +} diff --git a/common/src/com/android/utils/XmlUtils.java b/common/src/com/android/utils/XmlUtils.java new file mode 100644 index 0000000..7b51bc0 --- /dev/null +++ b/common/src/com/android/utils/XmlUtils.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2012 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.utils; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import java.util.HashSet; + +/** XML Utilities */ +public class XmlUtils { + /** Namespace used in XML files for Android attributes */ + public static final String ANDROID_URI = + "http://schemas.android.com/apk/res/android"; //$NON-NLS-1$ + /** Namespace used in XML files for Android Tooling attributes */ + public static final String TOOLS_URI = + "http://schemas.android.com/tools"; //$NON-NLS-1$ + /** URI of the reserved "xmlns" prefix */ + public final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; //$NON-NLS-1$ + /** The "xmlns" attribute name */ + public static final String XMLNS = "xmlns"; //$NON-NLS-1$ + /** The default prefix used for the {@link #XMLNS_URI} */ + public static final String XMLNS_PREFIX = "xmlns:"; //$NON-NLS-1$ + /** Qualified name of the xmlns android declaration element */ + public static final String XMLNS_ANDROID = "xmlns:android"; //$NON-NLS-1$ + /** The default prefix used for the {@link #ANDROID_URI} name space */ + public static final String ANDROID_NS_NAME = "android"; //$NON-NLS-1$ + /** The default prefix used for the {@link #ANDROID_URI} name space including the colon */ + public static final String ANDROID_NS_NAME_PREFIX = "android:"; //$NON-NLS-1$ + /** The default prefix used for the app */ + private static final String APP_PREFIX = "app"; //$NON-NLS-1$ + /** The "xmlns:" attribute prefix used for namespace declarations */ + public static final String XMLNS_COLON = "xmlns:"; //$NON-NLS-1$ + /** The entity for the ampersand character */ + public static final String AMP_ENTITY = "&"; //$NON-NLS-1$ + /** The entity for the quote character */ + public static final String QUOT_ENTITY = """; //$NON-NLS-1$ + /** The entity for the apostrophe character */ + public static final String APOS_ENTITY = "'"; //$NON-NLS-1$ + /** The entity for the less than character */ + public static final String LT_ENTITY = "<"; //$NON-NLS-1$ + /** The entity for the greater than character */ + public static final String GT_ENTITY = ">"; //$NON-NLS-1$ + + /** + * Returns the namespace prefix matching the requested namespace URI. + * If no such declaration is found, returns the default "android" prefix for + * the Android URI, and "app" for other URI's. + * + * @param node The current node. Must not be null. + * @param nsUri The namespace URI of which the prefix is to be found, + * e.g. SdkConstants.NS_RESOURCES + * @return The first prefix declared or the default "android" prefix + * (or "app" for non-Android URIs) + */ + @NonNull + public static String lookupNamespacePrefix(@NonNull Node node, @NonNull String nsUri) { + String defaultPrefix = ANDROID_URI.equals(nsUri) ? ANDROID_NS_NAME : APP_PREFIX; + return lookupNamespacePrefix(node, nsUri, defaultPrefix); + } + + /** + * Returns the namespace prefix matching the requested namespace URI. + * If no such declaration is found, returns the default "android" prefix. + * + * @param node The current node. Must not be null. + * @param nsUri The namespace URI of which the prefix is to be found, + * e.g. SdkConstants.NS_RESOURCES + * @param defaultPrefix The default prefix (root) to use if the namespace + * is not found. If null, do not create a new namespace + * if this URI is not defined for the document. + * @return The first prefix declared or the provided prefix (possibly with + * a number appended to avoid conflicts with existing prefixes. + */ + public static String lookupNamespacePrefix( + @Nullable Node node, @Nullable String nsUri, @Nullable String defaultPrefix) { + // Note: Node.lookupPrefix is not implemented in wst/xml/core NodeImpl.java + // The following code emulates this simple call: + // String prefix = node.lookupPrefix(SdkConstants.NS_RESOURCES); + + // if the requested URI is null, it denotes an attribute with no namespace. + if (nsUri == null) { + return null; + } + + // per XML specification, the "xmlns" URI is reserved + if (XMLNS_URI.equals(nsUri)) { + return XMLNS; + } + + HashSet visited = new HashSet(); + Document doc = node == null ? null : node.getOwnerDocument(); + + // Ask the document about it. This method may not be implemented by the Document. + String nsPrefix = null; + try { + nsPrefix = doc != null ? doc.lookupPrefix(nsUri) : null; + if (nsPrefix != null) { + return nsPrefix; + } + } catch (Throwable t) { + // ignore + } + + // If that failed, try to look it up manually. + // This also gathers prefixed in use in the case we want to generate a new one below. + for (; node != null && node.getNodeType() == Node.ELEMENT_NODE; + node = node.getParentNode()) { + NamedNodeMap attrs = node.getAttributes(); + for (int n = attrs.getLength() - 1; n >= 0; --n) { + Node attr = attrs.item(n); + if (XMLNS.equals(attr.getPrefix())) { + String uri = attr.getNodeValue(); + nsPrefix = attr.getLocalName(); + // Is this the URI we are looking for? If yes, we found its prefix. + if (nsUri.equals(uri)) { + return nsPrefix; + } + visited.add(nsPrefix); + } + } + } + + // Failed the find a prefix. Generate a new sensible default prefix, unless + // defaultPrefix was null in which case the caller does not want the document + // modified. + if (defaultPrefix == null) { + return null; + } + + // + // We need to make sure the prefix is not one that was declared in the scope + // visited above. Pick a unique prefix from the provided default prefix. + String prefix = defaultPrefix; + String base = prefix; + for (int i = 1; visited.contains(prefix); i++) { + prefix = base + Integer.toString(i); + } + // Also create & define this prefix/URI in the XML document as an attribute in the + // first element of the document. + if (doc != null) { + node = doc.getFirstChild(); + while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { + node = node.getNextSibling(); + } + if (node != null) { + // This doesn't work: + //Attr attr = doc.createAttributeNS(XMLNS_URI, prefix); + //attr.setPrefix(XMLNS); + // + // Xerces throws + //org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or + // change an object in a way which is incorrect with regard to namespaces. + // + // Instead pass in the concatenated prefix. (This is covered by + // the UiElementNodeTest#testCreateNameSpace() test.) + Attr attr = doc.createAttributeNS(XMLNS_URI, XMLNS_PREFIX + prefix); + attr.setValue(nsUri); + node.getAttributes().setNamedItemNS(attr); + } + } + + return prefix; + } + + /** + * Converts the given attribute value to an XML-attribute-safe value, meaning that + * single and double quotes are replaced with their corresponding XML entities. + * + * @param attrValue the value to be escaped + * @return the escaped value + */ + @NonNull + public static String toXmlAttributeValue(@NonNull String attrValue) { + for (int i = 0, n = attrValue.length(); i < n; i++) { + char c = attrValue.charAt(i); + if (c == '"' || c == '\'' || c == '<' || c == '&') { + StringBuilder sb = new StringBuilder(2 * attrValue.length()); + appendXmlAttributeValue(sb, attrValue); + return sb.toString(); + } + } + + return attrValue; + } + + /** + * Appends text to the given {@link StringBuilder} and escapes it as required for a + * DOM attribute node. + * + * @param sb the string builder + * @param attrValue the attribute value to be appended and escaped + */ + public static void appendXmlAttributeValue(@NonNull StringBuilder sb, + @NonNull String attrValue) { + int n = attrValue.length(); + // &, ", ' and < are illegal in attributes; see http://www.w3.org/TR/REC-xml/#NT-AttValue + // (' legal in a " string and " is legal in a ' string but here we'll stay on the safe + // side) + for (int i = 0; i < n; i++) { + char c = attrValue.charAt(i); + if (c == '"') { + sb.append(QUOT_ENTITY); + } else if (c == '<') { + sb.append(LT_ENTITY); + } else if (c == '\'') { + sb.append(APOS_ENTITY); + } else if (c == '&') { + sb.append(AMP_ENTITY); + } else { + sb.append(c); + } + } + } + + /** + * Appends text to the given {@link StringBuilder} and escapes it as required for a + * DOM text node. + * + * @param sb the string builder + * @param textValue the text value to be appended and escaped + */ + public static void appendXmlTextValue(@NonNull StringBuilder sb, @NonNull String textValue) { + for (int i = 0, n = textValue.length(); i < n; i++) { + char c = textValue.charAt(i); + if (c == '<') { + sb.append(LT_ENTITY); + } else if (c == '&') { + sb.append(AMP_ENTITY); + } else { + sb.append(c); + } + } + } +} -- cgit v1.1