Change to use dependencies (#481)
org.apache.commons:commons-lang3 de.upb.cs.swt:axml
This commit is contained in:
parent
d6d267d7f6
commit
20ba09408f
|
|
@ -66,6 +66,8 @@ val verName: String by rootProject.extra
|
|||
dependencies {
|
||||
implementation("dev.rikka.ndk:riru:${moduleMinRiruVersionName}")
|
||||
implementation("com.android.tools.build:apksig:4.1.3")
|
||||
implementation("org.apache.commons:commons-lang3:3.12.0")
|
||||
implementation("de.upb.cs.swt:axml:2.1.1")
|
||||
compileOnly(project(":hiddenapi-stubs"))
|
||||
compileOnly("androidx.annotation:annotation:1.2.0")
|
||||
implementation(project(":interface"))
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ import java.util.List;
|
|||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import external.org.apache.commons.lang3.ClassUtils;
|
||||
import external.org.apache.commons.lang3.reflect.MemberUtils;
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
import org.apache.commons.lang3.reflect.MemberUtilsX;
|
||||
|
||||
/**
|
||||
* Helpers that simplify hooking and calling methods/constructors, getting and settings fields, ...
|
||||
|
|
@ -437,9 +437,9 @@ public final class XposedHelpers {
|
|||
// compare name and parameters
|
||||
if (method.getName().equals(methodName) && ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true)) {
|
||||
// get accessible version of method
|
||||
if (bestMatch == null || MemberUtils.compareParameterTypes(
|
||||
method.getParameterTypes(),
|
||||
bestMatch.getParameterTypes(),
|
||||
if (bestMatch == null || MemberUtilsX.compareMethodFit(
|
||||
method,
|
||||
bestMatch,
|
||||
parameterTypes) < 0) {
|
||||
bestMatch = method;
|
||||
}
|
||||
|
|
@ -671,9 +671,9 @@ public final class XposedHelpers {
|
|||
// compare name and parameters
|
||||
if (ClassUtils.isAssignable(parameterTypes, constructor.getParameterTypes(), true)) {
|
||||
// get accessible version of method
|
||||
if (bestMatch == null || MemberUtils.compareParameterTypes(
|
||||
constructor.getParameterTypes(),
|
||||
bestMatch.getParameterTypes(),
|
||||
if (bestMatch == null || MemberUtilsX.compareConstructorFit(
|
||||
constructor,
|
||||
bestMatch,
|
||||
parameterTypes) < 0) {
|
||||
bestMatch = constructor;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,197 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3;
|
||||
|
||||
/**
|
||||
* <p>Operations on {@link java.lang.CharSequence} that are
|
||||
* {@code null} safe.</p>
|
||||
*
|
||||
* @see java.lang.CharSequence
|
||||
* @since 3.0
|
||||
* @version $Id: CharSequenceUtils.java 1199894 2011-11-09 17:53:59Z ggregory $
|
||||
*/
|
||||
public class CharSequenceUtils {
|
||||
|
||||
/**
|
||||
* <p>{@code CharSequenceUtils} instances should NOT be constructed in
|
||||
* standard programming. </p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean
|
||||
* instance to operate.</p>
|
||||
*/
|
||||
public CharSequenceUtils() {
|
||||
super();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Returns a new {@code CharSequence} that is a subsequence of this
|
||||
* sequence starting with the {@code char} value at the specified index.</p>
|
||||
*
|
||||
* <p>This provides the {@code CharSequence} equivalent to {@link String#substring(int)}.
|
||||
* The length (in {@code char}) of the returned sequence is {@code length() - start},
|
||||
* so if {@code start == end} then an empty sequence is returned.</p>
|
||||
*
|
||||
* @param cs the specified subsequence, null returns null
|
||||
* @param start the start index, inclusive, valid
|
||||
* @return a new subsequence, may be null
|
||||
* @throws IndexOutOfBoundsException if {@code start} is negative or if
|
||||
* {@code start} is greater than {@code length()}
|
||||
*/
|
||||
public static CharSequence subSequence(CharSequence cs, int start) {
|
||||
return cs == null ? null : cs.subSequence(start, cs.length());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Finds the first index in the {@code CharSequence} that matches the
|
||||
* specified character.</p>
|
||||
*
|
||||
* @param cs the {@code CharSequence} to be processed, not null
|
||||
* @param searchChar the char to be searched for
|
||||
* @param start the start index, negative starts at the string start
|
||||
* @return the index where the search char was found, -1 if not found
|
||||
*/
|
||||
static int indexOf(CharSequence cs, int searchChar, int start) {
|
||||
if (cs instanceof String) {
|
||||
return ((String) cs).indexOf(searchChar, start);
|
||||
} else {
|
||||
int sz = cs.length();
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
for (int i = start; i < sz; i++) {
|
||||
if (cs.charAt(i) == searchChar) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the indexOf(CharSequence methods) as a green implementation of indexOf.
|
||||
*
|
||||
* @param cs the {@code CharSequence} to be processed
|
||||
* @param searchChar the {@code CharSequence} to be searched for
|
||||
* @param start the start index
|
||||
* @return the index where the search sequence was found
|
||||
*/
|
||||
static int indexOf(CharSequence cs, CharSequence searchChar, int start) {
|
||||
return cs.toString().indexOf(searchChar.toString(), start);
|
||||
// if (cs instanceof String && searchChar instanceof String) {
|
||||
// // TODO: Do we assume searchChar is usually relatively small;
|
||||
// // If so then calling toString() on it is better than reverting to
|
||||
// // the green implementation in the else block
|
||||
// return ((String) cs).indexOf((String) searchChar, start);
|
||||
// } else {
|
||||
// // TODO: Implement rather than convert to String
|
||||
// return cs.toString().indexOf(searchChar.toString(), start);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Finds the last index in the {@code CharSequence} that matches the
|
||||
* specified character.</p>
|
||||
*
|
||||
* @param cs the {@code CharSequence} to be processed
|
||||
* @param searchChar the char to be searched for
|
||||
* @param start the start index, negative returns -1, beyond length starts at end
|
||||
* @return the index where the search char was found, -1 if not found
|
||||
*/
|
||||
static int lastIndexOf(CharSequence cs, int searchChar, int start) {
|
||||
if (cs instanceof String) {
|
||||
return ((String) cs).lastIndexOf(searchChar, start);
|
||||
} else {
|
||||
int sz = cs.length();
|
||||
if (start < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (start >= sz) {
|
||||
start = sz - 1;
|
||||
}
|
||||
for (int i = start; i >= 0; --i) {
|
||||
if (cs.charAt(i) == searchChar) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the lastIndexOf(CharSequence methods) as a green implementation of lastIndexOf
|
||||
*
|
||||
* @param cs the {@code CharSequence} to be processed
|
||||
* @param searchChar the {@code CharSequence} to be searched for
|
||||
* @param start the start index
|
||||
* @return the index where the search sequence was found
|
||||
*/
|
||||
static int lastIndexOf(CharSequence cs, CharSequence searchChar, int start) {
|
||||
return cs.toString().lastIndexOf(searchChar.toString(), start);
|
||||
// if (cs instanceof String && searchChar instanceof String) {
|
||||
// // TODO: Do we assume searchChar is usually relatively small;
|
||||
// // If so then calling toString() on it is better than reverting to
|
||||
// // the green implementation in the else block
|
||||
// return ((String) cs).lastIndexOf((String) searchChar, start);
|
||||
// } else {
|
||||
// // TODO: Implement rather than convert to String
|
||||
// return cs.toString().lastIndexOf(searchChar.toString(), start);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Green implementation of toCharArray.
|
||||
*
|
||||
* @param cs the {@code CharSequence} to be processed
|
||||
* @return the resulting char array
|
||||
*/
|
||||
static char[] toCharArray(CharSequence cs) {
|
||||
if (cs instanceof String) {
|
||||
return ((String) cs).toCharArray();
|
||||
} else {
|
||||
int sz = cs.length();
|
||||
char[] array = new char[cs.length()];
|
||||
for (int i = 0; i < sz; i++) {
|
||||
array[i] = cs.charAt(i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Green implementation of regionMatches.
|
||||
*
|
||||
* @param cs the {@code CharSequence} to be processed
|
||||
* @param ignoreCase whether or not to be case insensitive
|
||||
* @param thisStart the index to start on the {@code cs} CharSequence
|
||||
* @param substring the {@code CharSequence} to be looked for
|
||||
* @param start the index to start on the {@code substring} CharSequence
|
||||
* @param length character length of the region
|
||||
* @return whether the region matched
|
||||
*/
|
||||
static boolean regionMatches(CharSequence cs, boolean ignoreCase, int thisStart,
|
||||
CharSequence substring, int start, int length) {
|
||||
if (cs instanceof String && substring instanceof String) {
|
||||
return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length);
|
||||
} else {
|
||||
// TODO: Implement rather than convert to String
|
||||
return cs.toString().regionMatches(ignoreCase, thisStart, substring.toString(), start, length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,539 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3;
|
||||
|
||||
/**
|
||||
* <p>Operations on char primitives and Character objects.</p>
|
||||
*
|
||||
* <p>This class tries to handle {@code null} input gracefully.
|
||||
* An exception will not be thrown for a {@code null} input.
|
||||
* Each method documents its behaviour in more detail.</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
* @since 2.1
|
||||
* @version $Id: CharUtils.java 1158279 2011-08-16 14:06:45Z ggregory $
|
||||
*/
|
||||
public class CharUtils {
|
||||
|
||||
private static final String[] CHAR_STRING_ARRAY = new String[128];
|
||||
|
||||
/**
|
||||
* {@code \u000a} linefeed LF ('\n').
|
||||
*
|
||||
* @see <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089">JLF: Escape Sequences
|
||||
* for Character and String Literals</a>
|
||||
* @since 2.2
|
||||
*/
|
||||
public static final char LF = '\n';
|
||||
|
||||
/**
|
||||
* {@code \u000d} carriage return CR ('\r').
|
||||
*
|
||||
* @see <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089">JLF: Escape Sequences
|
||||
* for Character and String Literals</a>
|
||||
* @since 2.2
|
||||
*/
|
||||
public static final char CR = '\r';
|
||||
|
||||
|
||||
static {
|
||||
for (char c = 0; c < CHAR_STRING_ARRAY.length; c++) {
|
||||
CHAR_STRING_ARRAY[c] = String.valueOf(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>{@code CharUtils} instances should NOT be constructed in standard programming.
|
||||
* Instead, the class should be used as {@code CharUtils.toString('c');}.</p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean instance
|
||||
* to operate.</p>
|
||||
*/
|
||||
public CharUtils() {
|
||||
super();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the character to a Character.</p>
|
||||
*
|
||||
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||
* same Character object each time.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toCharacterObject(' ') = ' '
|
||||
* CharUtils.toCharacterObject('A') = 'A'
|
||||
* </pre>
|
||||
*
|
||||
* @deprecated Java 5 introduced {@link Character#valueOf(char)} which caches chars 0 through 127.
|
||||
* @param ch the character to convert
|
||||
* @return a Character of the specified character
|
||||
*/
|
||||
@Deprecated
|
||||
public static Character toCharacterObject(char ch) {
|
||||
return Character.valueOf(ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the String to a Character using the first character, returning
|
||||
* null for empty Strings.</p>
|
||||
*
|
||||
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||
* same Character object each time.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toCharacterObject(null) = null
|
||||
* CharUtils.toCharacterObject("") = null
|
||||
* CharUtils.toCharacterObject("A") = 'A'
|
||||
* CharUtils.toCharacterObject("BA") = 'B'
|
||||
* </pre>
|
||||
*
|
||||
* @param str the character to convert
|
||||
* @return the Character value of the first letter of the String
|
||||
*/
|
||||
public static Character toCharacterObject(String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return null;
|
||||
}
|
||||
return Character.valueOf(str.charAt(0));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the Character to a char throwing an exception for {@code null}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toChar(' ') = ' '
|
||||
* CharUtils.toChar('A') = 'A'
|
||||
* CharUtils.toChar(null) throws IllegalArgumentException
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to convert
|
||||
* @return the char value of the Character
|
||||
* @throws IllegalArgumentException if the Character is null
|
||||
*/
|
||||
public static char toChar(Character ch) {
|
||||
if (ch == null) {
|
||||
throw new IllegalArgumentException("The Character must not be null");
|
||||
}
|
||||
return ch.charValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the Character to a char handling {@code null}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toChar(null, 'X') = 'X'
|
||||
* CharUtils.toChar(' ', 'X') = ' '
|
||||
* CharUtils.toChar('A', 'X') = 'A'
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to convert
|
||||
* @param defaultValue the value to use if the Character is null
|
||||
* @return the char value of the Character or the default if null
|
||||
*/
|
||||
public static char toChar(Character ch, char defaultValue) {
|
||||
if (ch == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return ch.charValue();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the String to a char using the first character, throwing
|
||||
* an exception on empty Strings.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toChar("A") = 'A'
|
||||
* CharUtils.toChar("BA") = 'B'
|
||||
* CharUtils.toChar(null) throws IllegalArgumentException
|
||||
* CharUtils.toChar("") throws IllegalArgumentException
|
||||
* </pre>
|
||||
*
|
||||
* @param str the character to convert
|
||||
* @return the char value of the first letter of the String
|
||||
* @throws IllegalArgumentException if the String is empty
|
||||
*/
|
||||
public static char toChar(String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
throw new IllegalArgumentException("The String must not be empty");
|
||||
}
|
||||
return str.charAt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the String to a char using the first character, defaulting
|
||||
* the value on empty Strings.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toChar(null, 'X') = 'X'
|
||||
* CharUtils.toChar("", 'X') = 'X'
|
||||
* CharUtils.toChar("A", 'X') = 'A'
|
||||
* CharUtils.toChar("BA", 'X') = 'B'
|
||||
* </pre>
|
||||
*
|
||||
* @param str the character to convert
|
||||
* @param defaultValue the value to use if the Character is null
|
||||
* @return the char value of the first letter of the String or the default if null
|
||||
*/
|
||||
public static char toChar(String str, char defaultValue) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return str.charAt(0);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the character to the Integer it represents, throwing an
|
||||
* exception if the character is not numeric.</p>
|
||||
*
|
||||
* <p>This method coverts the char '1' to the int 1 and so on.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toIntValue('3') = 3
|
||||
* CharUtils.toIntValue('A') throws IllegalArgumentException
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to convert
|
||||
* @return the int value of the character
|
||||
* @throws IllegalArgumentException if the character is not ASCII numeric
|
||||
*/
|
||||
public static int toIntValue(char ch) {
|
||||
if (isAsciiNumeric(ch) == false) {
|
||||
throw new IllegalArgumentException("The character " + ch + " is not in the range '0' - '9'");
|
||||
}
|
||||
return ch - 48;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the character to the Integer it represents, throwing an
|
||||
* exception if the character is not numeric.</p>
|
||||
*
|
||||
* <p>This method coverts the char '1' to the int 1 and so on.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toIntValue('3', -1) = 3
|
||||
* CharUtils.toIntValue('A', -1) = -1
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to convert
|
||||
* @param defaultValue the default value to use if the character is not numeric
|
||||
* @return the int value of the character
|
||||
*/
|
||||
public static int toIntValue(char ch, int defaultValue) {
|
||||
if (isAsciiNumeric(ch) == false) {
|
||||
return defaultValue;
|
||||
}
|
||||
return ch - 48;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the character to the Integer it represents, throwing an
|
||||
* exception if the character is not numeric.</p>
|
||||
*
|
||||
* <p>This method coverts the char '1' to the int 1 and so on.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toIntValue('3') = 3
|
||||
* CharUtils.toIntValue(null) throws IllegalArgumentException
|
||||
* CharUtils.toIntValue('A') throws IllegalArgumentException
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to convert, not null
|
||||
* @return the int value of the character
|
||||
* @throws IllegalArgumentException if the Character is not ASCII numeric or is null
|
||||
*/
|
||||
public static int toIntValue(Character ch) {
|
||||
if (ch == null) {
|
||||
throw new IllegalArgumentException("The character must not be null");
|
||||
}
|
||||
return toIntValue(ch.charValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the character to the Integer it represents, throwing an
|
||||
* exception if the character is not numeric.</p>
|
||||
*
|
||||
* <p>This method coverts the char '1' to the int 1 and so on.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toIntValue(null, -1) = -1
|
||||
* CharUtils.toIntValue('3', -1) = 3
|
||||
* CharUtils.toIntValue('A', -1) = -1
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to convert
|
||||
* @param defaultValue the default value to use if the character is not numeric
|
||||
* @return the int value of the character
|
||||
*/
|
||||
public static int toIntValue(Character ch, int defaultValue) {
|
||||
if (ch == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return toIntValue(ch.charValue(), defaultValue);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the character to a String that contains the one character.</p>
|
||||
*
|
||||
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||
* same String object each time.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toString(' ') = " "
|
||||
* CharUtils.toString('A') = "A"
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to convert
|
||||
* @return a String containing the one specified character
|
||||
*/
|
||||
public static String toString(char ch) {
|
||||
if (ch < 128) {
|
||||
return CHAR_STRING_ARRAY[ch];
|
||||
}
|
||||
return new String(new char[] {ch});
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the character to a String that contains the one character.</p>
|
||||
*
|
||||
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||
* same String object each time.</p>
|
||||
*
|
||||
* <p>If {@code null} is passed in, {@code null} will be returned.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toString(null) = null
|
||||
* CharUtils.toString(' ') = " "
|
||||
* CharUtils.toString('A') = "A"
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to convert
|
||||
* @return a String containing the one specified character
|
||||
*/
|
||||
public static String toString(Character ch) {
|
||||
if (ch == null) {
|
||||
return null;
|
||||
}
|
||||
return toString(ch.charValue());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the string to the Unicode format '\u0020'.</p>
|
||||
*
|
||||
* <p>This format is the Java source code format.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.unicodeEscaped(' ') = "\u0020"
|
||||
* CharUtils.unicodeEscaped('A') = "\u0041"
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to convert
|
||||
* @return the escaped Unicode string
|
||||
*/
|
||||
public static String unicodeEscaped(char ch) {
|
||||
if (ch < 0x10) {
|
||||
return "\\u000" + Integer.toHexString(ch);
|
||||
} else if (ch < 0x100) {
|
||||
return "\\u00" + Integer.toHexString(ch);
|
||||
} else if (ch < 0x1000) {
|
||||
return "\\u0" + Integer.toHexString(ch);
|
||||
}
|
||||
return "\\u" + Integer.toHexString(ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the string to the Unicode format '\u0020'.</p>
|
||||
*
|
||||
* <p>This format is the Java source code format.</p>
|
||||
*
|
||||
* <p>If {@code null} is passed in, {@code null} will be returned.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.unicodeEscaped(null) = null
|
||||
* CharUtils.unicodeEscaped(' ') = "\u0020"
|
||||
* CharUtils.unicodeEscaped('A') = "\u0041"
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to convert, may be null
|
||||
* @return the escaped Unicode string, null if null input
|
||||
*/
|
||||
public static String unicodeEscaped(Character ch) {
|
||||
if (ch == null) {
|
||||
return null;
|
||||
}
|
||||
return unicodeEscaped(ch.charValue());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAscii('a') = true
|
||||
* CharUtils.isAscii('A') = true
|
||||
* CharUtils.isAscii('3') = true
|
||||
* CharUtils.isAscii('-') = true
|
||||
* CharUtils.isAscii('\n') = true
|
||||
* CharUtils.isAscii('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to check
|
||||
* @return true if less than 128
|
||||
*/
|
||||
public static boolean isAscii(char ch) {
|
||||
return ch < 128;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit printable.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiPrintable('a') = true
|
||||
* CharUtils.isAsciiPrintable('A') = true
|
||||
* CharUtils.isAsciiPrintable('3') = true
|
||||
* CharUtils.isAsciiPrintable('-') = true
|
||||
* CharUtils.isAsciiPrintable('\n') = false
|
||||
* CharUtils.isAsciiPrintable('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to check
|
||||
* @return true if between 32 and 126 inclusive
|
||||
*/
|
||||
public static boolean isAsciiPrintable(char ch) {
|
||||
return ch >= 32 && ch < 127;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit control.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiControl('a') = false
|
||||
* CharUtils.isAsciiControl('A') = false
|
||||
* CharUtils.isAsciiControl('3') = false
|
||||
* CharUtils.isAsciiControl('-') = false
|
||||
* CharUtils.isAsciiControl('\n') = true
|
||||
* CharUtils.isAsciiControl('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to check
|
||||
* @return true if less than 32 or equals 127
|
||||
*/
|
||||
public static boolean isAsciiControl(char ch) {
|
||||
return ch < 32 || ch == 127;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit alphabetic.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiAlpha('a') = true
|
||||
* CharUtils.isAsciiAlpha('A') = true
|
||||
* CharUtils.isAsciiAlpha('3') = false
|
||||
* CharUtils.isAsciiAlpha('-') = false
|
||||
* CharUtils.isAsciiAlpha('\n') = false
|
||||
* CharUtils.isAsciiAlpha('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to check
|
||||
* @return true if between 65 and 90 or 97 and 122 inclusive
|
||||
*/
|
||||
public static boolean isAsciiAlpha(char ch) {
|
||||
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit alphabetic upper case.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiAlphaUpper('a') = false
|
||||
* CharUtils.isAsciiAlphaUpper('A') = true
|
||||
* CharUtils.isAsciiAlphaUpper('3') = false
|
||||
* CharUtils.isAsciiAlphaUpper('-') = false
|
||||
* CharUtils.isAsciiAlphaUpper('\n') = false
|
||||
* CharUtils.isAsciiAlphaUpper('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to check
|
||||
* @return true if between 65 and 90 inclusive
|
||||
*/
|
||||
public static boolean isAsciiAlphaUpper(char ch) {
|
||||
return ch >= 'A' && ch <= 'Z';
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit alphabetic lower case.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiAlphaLower('a') = true
|
||||
* CharUtils.isAsciiAlphaLower('A') = false
|
||||
* CharUtils.isAsciiAlphaLower('3') = false
|
||||
* CharUtils.isAsciiAlphaLower('-') = false
|
||||
* CharUtils.isAsciiAlphaLower('\n') = false
|
||||
* CharUtils.isAsciiAlphaLower('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to check
|
||||
* @return true if between 97 and 122 inclusive
|
||||
*/
|
||||
public static boolean isAsciiAlphaLower(char ch) {
|
||||
return ch >= 'a' && ch <= 'z';
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit numeric.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiNumeric('a') = false
|
||||
* CharUtils.isAsciiNumeric('A') = false
|
||||
* CharUtils.isAsciiNumeric('3') = true
|
||||
* CharUtils.isAsciiNumeric('-') = false
|
||||
* CharUtils.isAsciiNumeric('\n') = false
|
||||
* CharUtils.isAsciiNumeric('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to check
|
||||
* @return true if between 48 and 57 inclusive
|
||||
*/
|
||||
public static boolean isAsciiNumeric(char ch) {
|
||||
return ch >= '0' && ch <= '9';
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit numeric.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiAlphanumeric('a') = true
|
||||
* CharUtils.isAsciiAlphanumeric('A') = true
|
||||
* CharUtils.isAsciiAlphanumeric('3') = true
|
||||
* CharUtils.isAsciiAlphanumeric('-') = false
|
||||
* CharUtils.isAsciiAlphanumeric('\n') = false
|
||||
* CharUtils.isAsciiAlphanumeric('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to check
|
||||
* @return true if between 48 and 57 or 65 and 90 or 97 and 122 inclusive
|
||||
*/
|
||||
public static boolean isAsciiAlphanumeric(char ch) {
|
||||
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9');
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,168 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3;
|
||||
|
||||
/**
|
||||
* <p>An enum representing all the versions of the Java specification.
|
||||
* This is intended to mirror available values from the
|
||||
* <em>java.specification.version</em> System property. </p>
|
||||
*
|
||||
* @since 3.0
|
||||
* @version $Id: $
|
||||
*/
|
||||
public enum JavaVersion {
|
||||
|
||||
/**
|
||||
* The Java version reported by Android. This is not an official Java version number.
|
||||
*/
|
||||
JAVA_0_9(1.5f, "0.9"),
|
||||
|
||||
/**
|
||||
* Java 1.1.
|
||||
*/
|
||||
JAVA_1_1(1.1f, "1.1"),
|
||||
|
||||
/**
|
||||
* Java 1.2.
|
||||
*/
|
||||
JAVA_1_2(1.2f, "1.2"),
|
||||
|
||||
/**
|
||||
* Java 1.3.
|
||||
*/
|
||||
JAVA_1_3(1.3f, "1.3"),
|
||||
|
||||
/**
|
||||
* Java 1.4.
|
||||
*/
|
||||
JAVA_1_4(1.4f, "1.4"),
|
||||
|
||||
/**
|
||||
* Java 1.5.
|
||||
*/
|
||||
JAVA_1_5(1.5f, "1.5"),
|
||||
|
||||
/**
|
||||
* Java 1.6.
|
||||
*/
|
||||
JAVA_1_6(1.6f, "1.6"),
|
||||
|
||||
/**
|
||||
* Java 1.7.
|
||||
*/
|
||||
JAVA_1_7(1.7f, "1.7"),
|
||||
|
||||
/**
|
||||
* Java 1.8.
|
||||
*/
|
||||
JAVA_1_8(1.8f, "1.8");
|
||||
|
||||
/**
|
||||
* The float value.
|
||||
*/
|
||||
private float value;
|
||||
/**
|
||||
* The standard name.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param value the float value
|
||||
* @param name the standard name, not null
|
||||
*/
|
||||
JavaVersion(final float value, final String name) {
|
||||
this.value = value;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Whether this version of Java is at least the version of Java passed in.</p>
|
||||
*
|
||||
* <p>For example:<br />
|
||||
* {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}<p>
|
||||
*
|
||||
* @param requiredVersion the version to check against, not null
|
||||
* @return true if this version is equal to or greater than the specified version
|
||||
*/
|
||||
public boolean atLeast(JavaVersion requiredVersion) {
|
||||
return this.value >= requiredVersion.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the given string with a Java version number to the
|
||||
* corresponding constant of this enumeration class. This method is used
|
||||
* internally.
|
||||
*
|
||||
* @param nom the Java version as string
|
||||
* @return the corresponding enumeration constant or <b>null</b> if the
|
||||
* version is unknown
|
||||
*/
|
||||
// helper for static importing
|
||||
static JavaVersion getJavaVersion(final String nom) {
|
||||
return get(nom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the given string with a Java version number to the
|
||||
* corresponding constant of this enumeration class. This method is used
|
||||
* internally.
|
||||
*
|
||||
* @param nom the Java version as string
|
||||
* @return the corresponding enumeration constant or <b>null</b> if the
|
||||
* version is unknown
|
||||
*/
|
||||
static JavaVersion get(final String nom) {
|
||||
if ("0.9".equals(nom)) {
|
||||
return JAVA_0_9;
|
||||
} else if ("1.1".equals(nom)) {
|
||||
return JAVA_1_1;
|
||||
} else if ("1.2".equals(nom)) {
|
||||
return JAVA_1_2;
|
||||
} else if ("1.3".equals(nom)) {
|
||||
return JAVA_1_3;
|
||||
} else if ("1.4".equals(nom)) {
|
||||
return JAVA_1_4;
|
||||
} else if ("1.5".equals(nom)) {
|
||||
return JAVA_1_5;
|
||||
} else if ("1.6".equals(nom)) {
|
||||
return JAVA_1_6;
|
||||
} else if ("1.7".equals(nom)) {
|
||||
return JAVA_1_7;
|
||||
} else if ("1.8".equals(nom)) {
|
||||
return JAVA_1_8;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>The string value is overridden to return the standard name.</p>
|
||||
*
|
||||
* <p>For example, <code>"1.5"</code>.</p>
|
||||
*
|
||||
* @return the name, not null
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
This is the original source code of the Apache Commons Lang library version 3.1
|
||||
as downloaded from http://commons.apache.org/lang/download_lang.cgi, except for
|
||||
these modifications:
|
||||
- Class MemberUtils changed to public
|
||||
- Method compareParameterTypes in MemberUtils changed to public
|
||||
- Prefix "external." for packages to avoid conflicts with other apps
|
||||
- Removed unused sub-packages for smaller file size:
|
||||
concurrent/
|
||||
event/
|
||||
math/
|
||||
text/
|
||||
time/
|
||||
- Removed unused classes for smaller file size:
|
||||
AnnotationUtils.java
|
||||
BitField.java
|
||||
BooleanUtils.java
|
||||
CharEncoding.java
|
||||
CharRange.java
|
||||
CharSet.java
|
||||
CharSetUtils.java
|
||||
EnumUtils.java
|
||||
LocaleUtils.java
|
||||
RandomStringUtils.java
|
||||
Range.java
|
||||
SerializationException.java
|
||||
SerializationUtils.java
|
||||
StringEscapeUtils.java
|
||||
builder/StandardToStringStyle.java
|
||||
exception/ContextedException.java
|
||||
exception/ContextedRuntimeException.java
|
||||
exception/DefaultExceptionContext.java
|
||||
exception/ExceptionContext.java
|
||||
exception/ExceptionUtils.java
|
||||
mutable/MutableBoolean.java
|
||||
mutable/MutableByte.java
|
||||
mutable/MutableDouble.java
|
||||
mutable/MutableFloat.java
|
||||
mutable/MutableLong.java
|
||||
mutable/MutableObject.java
|
||||
mutable/MutableShort.java
|
||||
reflect/ConstructorUtils.java
|
||||
reflect/FieldUtils.java
|
||||
reflect/TypeUtils.java
|
||||
tuple/MutablePair.java
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
Apache Commons Lang
|
||||
Copyright 2001-2011 The Apache Software Foundation
|
||||
|
||||
This product includes software developed by
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
||||
This product includes software from the Spring Framework,
|
||||
under the Apache License 2.0 (see: StringUtils.containsWhitespace())
|
||||
|
|
@ -1,609 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
|
||||
import external.org.apache.commons.lang3.exception.CloneFailedException;
|
||||
import external.org.apache.commons.lang3.mutable.MutableInt;
|
||||
|
||||
/**
|
||||
* <p>Operations on {@code Object}.</p>
|
||||
*
|
||||
* <p>This class tries to handle {@code null} input gracefully.
|
||||
* An exception will generally not be thrown for a {@code null} input.
|
||||
* Each method documents its behaviour in more detail.</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
* @since 1.0
|
||||
* @version $Id: ObjectUtils.java 1199894 2011-11-09 17:53:59Z ggregory $
|
||||
*/
|
||||
//@Immutable
|
||||
public class ObjectUtils {
|
||||
|
||||
/**
|
||||
* <p>Singleton used as a {@code null} placeholder where
|
||||
* {@code null} has another meaning.</p>
|
||||
*
|
||||
* <p>For example, in a {@code HashMap} the
|
||||
* {@link java.util.HashMap#get(java.lang.Object)} method returns
|
||||
* {@code null} if the {@code Map} contains {@code null} or if there
|
||||
* is no matching key. The {@code Null} placeholder can be used to
|
||||
* distinguish between these two cases.</p>
|
||||
*
|
||||
* <p>Another example is {@code Hashtable}, where {@code null}
|
||||
* cannot be stored.</p>
|
||||
*
|
||||
* <p>This instance is Serializable.</p>
|
||||
*/
|
||||
public static final Null NULL = new Null();
|
||||
|
||||
/**
|
||||
* <p>{@code ObjectUtils} instances should NOT be constructed in
|
||||
* standard programming. Instead, the static methods on the class should
|
||||
* be used, such as {@code ObjectUtils.defaultIfNull("a","b");}.</p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean
|
||||
* instance to operate.</p>
|
||||
*/
|
||||
public ObjectUtils() {
|
||||
super();
|
||||
}
|
||||
|
||||
// Defaulting
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Returns a default value if the object passed is {@code null}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectUtils.defaultIfNull(null, null) = null
|
||||
* ObjectUtils.defaultIfNull(null, "") = ""
|
||||
* ObjectUtils.defaultIfNull(null, "zz") = "zz"
|
||||
* ObjectUtils.defaultIfNull("abc", *) = "abc"
|
||||
* ObjectUtils.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
|
||||
* </pre>
|
||||
*
|
||||
* @param <T> the type of the object
|
||||
* @param object the {@code Object} to test, may be {@code null}
|
||||
* @param defaultValue the default value to return, may be {@code null}
|
||||
* @return {@code object} if it is not {@code null}, defaultValue otherwise
|
||||
*/
|
||||
public static <T> T defaultIfNull(T object, T defaultValue) {
|
||||
return object != null ? object : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the first value in the array which is not {@code null}.
|
||||
* If all the values are {@code null} or the array is {@code null}
|
||||
* or empty then {@code null} is returned.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectUtils.firstNonNull(null, null) = null
|
||||
* ObjectUtils.firstNonNull(null, "") = ""
|
||||
* ObjectUtils.firstNonNull(null, null, "") = ""
|
||||
* ObjectUtils.firstNonNull(null, "zz") = "zz"
|
||||
* ObjectUtils.firstNonNull("abc", *) = "abc"
|
||||
* ObjectUtils.firstNonNull(null, "xyz", *) = "xyz"
|
||||
* ObjectUtils.firstNonNull(Boolean.TRUE, *) = Boolean.TRUE
|
||||
* ObjectUtils.firstNonNull() = null
|
||||
* </pre>
|
||||
*
|
||||
* @param <T> the component type of the array
|
||||
* @param values the values to test, may be {@code null} or empty
|
||||
* @return the first value from {@code values} which is not {@code null},
|
||||
* or {@code null} if there are no non-null values
|
||||
* @since 3.0
|
||||
*/
|
||||
public static <T> T firstNonNull(T... values) {
|
||||
if (values != null) {
|
||||
for (T val : values) {
|
||||
if (val != null) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Null-safe equals/hashCode
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Compares two objects for equality, where either one or both
|
||||
* objects may be {@code null}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectUtils.equals(null, null) = true
|
||||
* ObjectUtils.equals(null, "") = false
|
||||
* ObjectUtils.equals("", null) = false
|
||||
* ObjectUtils.equals("", "") = true
|
||||
* ObjectUtils.equals(Boolean.TRUE, null) = false
|
||||
* ObjectUtils.equals(Boolean.TRUE, "true") = false
|
||||
* ObjectUtils.equals(Boolean.TRUE, Boolean.TRUE) = true
|
||||
* ObjectUtils.equals(Boolean.TRUE, Boolean.FALSE) = false
|
||||
* </pre>
|
||||
*
|
||||
* @param object1 the first object, may be {@code null}
|
||||
* @param object2 the second object, may be {@code null}
|
||||
* @return {@code true} if the values of both objects are the same
|
||||
*/
|
||||
public static boolean equals(Object object1, Object object2) {
|
||||
if (object1 == object2) {
|
||||
return true;
|
||||
}
|
||||
if (object1 == null || object2 == null) {
|
||||
return false;
|
||||
}
|
||||
return object1.equals(object2);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Compares two objects for inequality, where either one or both
|
||||
* objects may be {@code null}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectUtils.notEqual(null, null) = false
|
||||
* ObjectUtils.notEqual(null, "") = true
|
||||
* ObjectUtils.notEqual("", null) = true
|
||||
* ObjectUtils.notEqual("", "") = false
|
||||
* ObjectUtils.notEqual(Boolean.TRUE, null) = true
|
||||
* ObjectUtils.notEqual(Boolean.TRUE, "true") = true
|
||||
* ObjectUtils.notEqual(Boolean.TRUE, Boolean.TRUE) = false
|
||||
* ObjectUtils.notEqual(Boolean.TRUE, Boolean.FALSE) = true
|
||||
* </pre>
|
||||
*
|
||||
* @param object1 the first object, may be {@code null}
|
||||
* @param object2 the second object, may be {@code null}
|
||||
* @return {@code false} if the values of both objects are the same
|
||||
*/
|
||||
public static boolean notEqual(Object object1, Object object2) {
|
||||
return ObjectUtils.equals(object1, object2) == false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the hash code of an object returning zero when the
|
||||
* object is {@code null}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectUtils.hashCode(null) = 0
|
||||
* ObjectUtils.hashCode(obj) = obj.hashCode()
|
||||
* </pre>
|
||||
*
|
||||
* @param obj the object to obtain the hash code of, may be {@code null}
|
||||
* @return the hash code of the object, or zero if null
|
||||
* @since 2.1
|
||||
*/
|
||||
public static int hashCode(Object obj) {
|
||||
// hashCode(Object) retained for performance, as hash code is often critical
|
||||
return obj == null ? 0 : obj.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the hash code for multiple objects.</p>
|
||||
*
|
||||
* <p>This allows a hash code to be rapidly calculated for a number of objects.
|
||||
* The hash code for a single object is the <em>not</em> same as {@link #hashCode(Object)}.
|
||||
* The hash code for multiple objects is the same as that calculated by an
|
||||
* {@code ArrayList} containing the specified objects.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectUtils.hashCodeMulti() = 1
|
||||
* ObjectUtils.hashCodeMulti((Object[]) null) = 1
|
||||
* ObjectUtils.hashCodeMulti(a) = 31 + a.hashCode()
|
||||
* ObjectUtils.hashCodeMulti(a,b) = (31 + a.hashCode()) * 31 + b.hashCode()
|
||||
* ObjectUtils.hashCodeMulti(a,b,c) = ((31 + a.hashCode()) * 31 + b.hashCode()) * 31 + c.hashCode()
|
||||
* </pre>
|
||||
*
|
||||
* @param objects the objects to obtain the hash code of, may be {@code null}
|
||||
* @return the hash code of the objects, or zero if null
|
||||
* @since 3.0
|
||||
*/
|
||||
public static int hashCodeMulti(Object... objects) {
|
||||
int hash = 1;
|
||||
if (objects != null) {
|
||||
for (Object object : objects) {
|
||||
hash = hash * 31 + ObjectUtils.hashCode(object);
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// Identity ToString
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Gets the toString that would be produced by {@code Object}
|
||||
* if a class did not override toString itself. {@code null}
|
||||
* will return {@code null}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectUtils.identityToString(null) = null
|
||||
* ObjectUtils.identityToString("") = "java.lang.String@1e23"
|
||||
* ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
|
||||
* </pre>
|
||||
*
|
||||
* @param object the object to create a toString for, may be
|
||||
* {@code null}
|
||||
* @return the default toString text, or {@code null} if
|
||||
* {@code null} passed in
|
||||
*/
|
||||
public static String identityToString(Object object) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
identityToString(buffer, object);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Appends the toString that would be produced by {@code Object}
|
||||
* if a class did not override toString itself. {@code null}
|
||||
* will throw a NullPointerException for either of the two parameters. </p>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectUtils.identityToString(buf, "") = buf.append("java.lang.String@1e23"
|
||||
* ObjectUtils.identityToString(buf, Boolean.TRUE) = buf.append("java.lang.Boolean@7fa"
|
||||
* ObjectUtils.identityToString(buf, Boolean.TRUE) = buf.append("java.lang.Boolean@7fa")
|
||||
* </pre>
|
||||
*
|
||||
* @param buffer the buffer to append to
|
||||
* @param object the object to create a toString for
|
||||
* @since 2.4
|
||||
*/
|
||||
public static void identityToString(StringBuffer buffer, Object object) {
|
||||
if (object == null) {
|
||||
throw new NullPointerException("Cannot get the toString of a null identity");
|
||||
}
|
||||
buffer.append(object.getClass().getName())
|
||||
.append('@')
|
||||
.append(Integer.toHexString(System.identityHashCode(object)));
|
||||
}
|
||||
|
||||
// ToString
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Gets the {@code toString} of an {@code Object} returning
|
||||
* an empty string ("") if {@code null} input.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectUtils.toString(null) = ""
|
||||
* ObjectUtils.toString("") = ""
|
||||
* ObjectUtils.toString("bat") = "bat"
|
||||
* ObjectUtils.toString(Boolean.TRUE) = "true"
|
||||
* </pre>
|
||||
*
|
||||
* @see StringUtils#defaultString(String)
|
||||
* @see String#valueOf(Object)
|
||||
* @param obj the Object to {@code toString}, may be null
|
||||
* @return the passed in Object's toString, or nullStr if {@code null} input
|
||||
* @since 2.0
|
||||
*/
|
||||
public static String toString(Object obj) {
|
||||
return obj == null ? "" : obj.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the {@code toString} of an {@code Object} returning
|
||||
* a specified text if {@code null} input.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectUtils.toString(null, null) = null
|
||||
* ObjectUtils.toString(null, "null") = "null"
|
||||
* ObjectUtils.toString("", "null") = ""
|
||||
* ObjectUtils.toString("bat", "null") = "bat"
|
||||
* ObjectUtils.toString(Boolean.TRUE, "null") = "true"
|
||||
* </pre>
|
||||
*
|
||||
* @see StringUtils#defaultString(String,String)
|
||||
* @see String#valueOf(Object)
|
||||
* @param obj the Object to {@code toString}, may be null
|
||||
* @param nullStr the String to return if {@code null} input, may be null
|
||||
* @return the passed in Object's toString, or nullStr if {@code null} input
|
||||
* @since 2.0
|
||||
*/
|
||||
public static String toString(Object obj, String nullStr) {
|
||||
return obj == null ? nullStr : obj.toString();
|
||||
}
|
||||
|
||||
// Comparable
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Null safe comparison of Comparables.</p>
|
||||
*
|
||||
* @param <T> type of the values processed by this method
|
||||
* @param values the set of comparable values, may be null
|
||||
* @return
|
||||
* <ul>
|
||||
* <li>If any objects are non-null and unequal, the lesser object.
|
||||
* <li>If all objects are non-null and equal, the first.
|
||||
* <li>If any of the comparables are null, the lesser of the non-null objects.
|
||||
* <li>If all the comparables are null, null is returned.
|
||||
* </ul>
|
||||
*/
|
||||
public static <T extends Comparable<? super T>> T min(T... values) {
|
||||
T result = null;
|
||||
if (values != null) {
|
||||
for (T value : values) {
|
||||
if (compare(value, result, true) < 0) {
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Null safe comparison of Comparables.</p>
|
||||
*
|
||||
* @param <T> type of the values processed by this method
|
||||
* @param values the set of comparable values, may be null
|
||||
* @return
|
||||
* <ul>
|
||||
* <li>If any objects are non-null and unequal, the greater object.
|
||||
* <li>If all objects are non-null and equal, the first.
|
||||
* <li>If any of the comparables are null, the greater of the non-null objects.
|
||||
* <li>If all the comparables are null, null is returned.
|
||||
* </ul>
|
||||
*/
|
||||
public static <T extends Comparable<? super T>> T max(T... values) {
|
||||
T result = null;
|
||||
if (values != null) {
|
||||
for (T value : values) {
|
||||
if (compare(value, result, false) > 0) {
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Null safe comparison of Comparables.
|
||||
* {@code null} is assumed to be less than a non-{@code null} value.</p>
|
||||
*
|
||||
* @param <T> type of the values processed by this method
|
||||
* @param c1 the first comparable, may be null
|
||||
* @param c2 the second comparable, may be null
|
||||
* @return a negative value if c1 < c2, zero if c1 = c2
|
||||
* and a positive value if c1 > c2
|
||||
*/
|
||||
public static <T extends Comparable<? super T>> int compare(T c1, T c2) {
|
||||
return compare(c1, c2, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Null safe comparison of Comparables.</p>
|
||||
*
|
||||
* @param <T> type of the values processed by this method
|
||||
* @param c1 the first comparable, may be null
|
||||
* @param c2 the second comparable, may be null
|
||||
* @param nullGreater if true {@code null} is considered greater
|
||||
* than a non-{@code null} value or if false {@code null} is
|
||||
* considered less than a Non-{@code null} value
|
||||
* @return a negative value if c1 < c2, zero if c1 = c2
|
||||
* and a positive value if c1 > c2
|
||||
* @see java.util.Comparator#compare(Object, Object)
|
||||
*/
|
||||
public static <T extends Comparable<? super T>> int compare(T c1, T c2, boolean nullGreater) {
|
||||
if (c1 == c2) {
|
||||
return 0;
|
||||
} else if (c1 == null) {
|
||||
return nullGreater ? 1 : -1;
|
||||
} else if (c2 == null) {
|
||||
return nullGreater ? -1 : 1;
|
||||
}
|
||||
return c1.compareTo(c2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the "best guess" middle value among comparables. If there is an even
|
||||
* number of total values, the lower of the two middle values will be returned.
|
||||
* @param <T> type of values processed by this method
|
||||
* @param items to compare
|
||||
* @return T at middle position
|
||||
* @throws NullPointerException if items is {@code null}
|
||||
* @throws IllegalArgumentException if items is empty or contains {@code null} values
|
||||
* @since 3.0.1
|
||||
*/
|
||||
public static <T extends Comparable<? super T>> T median(T... items) {
|
||||
Validate.notEmpty(items);
|
||||
Validate.noNullElements(items);
|
||||
TreeSet<T> sort = new TreeSet<T>();
|
||||
Collections.addAll(sort, items);
|
||||
@SuppressWarnings("unchecked") //we know all items added were T instances
|
||||
T result = (T) sort.toArray()[(sort.size() - 1) / 2];
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the "best guess" middle value among comparables. If there is an even
|
||||
* number of total values, the lower of the two middle values will be returned.
|
||||
* @param <T> type of values processed by this method
|
||||
* @param comparator to use for comparisons
|
||||
* @param items to compare
|
||||
* @return T at middle position
|
||||
* @throws NullPointerException if items or comparator is {@code null}
|
||||
* @throws IllegalArgumentException if items is empty or contains {@code null} values
|
||||
* @since 3.0.1
|
||||
*/
|
||||
public static <T> T median(Comparator<T> comparator, T... items) {
|
||||
Validate.notEmpty(items, "null/empty items");
|
||||
Validate.noNullElements(items);
|
||||
Validate.notNull(comparator, "null comparator");
|
||||
TreeSet<T> sort = new TreeSet<T>(comparator);
|
||||
Collections.addAll(sort, items);
|
||||
@SuppressWarnings("unchecked") //we know all items added were T instances
|
||||
T result = (T) sort.toArray()[(sort.size() - 1) / 2];
|
||||
return result;
|
||||
}
|
||||
|
||||
// Mode
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Find the most frequently occurring item.
|
||||
*
|
||||
* @param <T> type of values processed by this method
|
||||
* @param items to check
|
||||
* @return most populous T, {@code null} if non-unique or no items supplied
|
||||
* @since 3.0.1
|
||||
*/
|
||||
public static <T> T mode(T... items) {
|
||||
if (ArrayUtils.isNotEmpty(items)) {
|
||||
HashMap<T, MutableInt> occurrences = new HashMap<T, MutableInt>(items.length);
|
||||
for (T t : items) {
|
||||
MutableInt count = occurrences.get(t);
|
||||
if (count == null) {
|
||||
occurrences.put(t, new MutableInt(1));
|
||||
} else {
|
||||
count.increment();
|
||||
}
|
||||
}
|
||||
T result = null;
|
||||
int max = 0;
|
||||
for (Map.Entry<T, MutableInt> e : occurrences.entrySet()) {
|
||||
int cmp = e.getValue().intValue();
|
||||
if (cmp == max) {
|
||||
result = null;
|
||||
} else if (cmp > max) {
|
||||
max = cmp;
|
||||
result = e.getKey();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// cloning
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Clone an object.</p>
|
||||
*
|
||||
* @param <T> the type of the object
|
||||
* @param obj the object to clone, null returns null
|
||||
* @return the clone if the object implements {@link Cloneable} otherwise {@code null}
|
||||
* @throws CloneFailedException if the object is cloneable and the clone operation fails
|
||||
* @since 3.0
|
||||
*/
|
||||
public static <T> T clone(final T obj) {
|
||||
if (obj instanceof Cloneable) {
|
||||
final Object result;
|
||||
if (obj.getClass().isArray()) {
|
||||
final Class<?> componentType = obj.getClass().getComponentType();
|
||||
if (!componentType.isPrimitive()) {
|
||||
result = ((Object[]) obj).clone();
|
||||
} else {
|
||||
int length = Array.getLength(obj);
|
||||
result = Array.newInstance(componentType, length);
|
||||
while (length-- > 0) {
|
||||
Array.set(result, length, Array.get(obj, length));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
final Method clone = obj.getClass().getMethod("clone");
|
||||
result = clone.invoke(obj);
|
||||
} catch (final NoSuchMethodException e) {
|
||||
throw new CloneFailedException("Cloneable type "
|
||||
+ obj.getClass().getName()
|
||||
+ " has no clone method", e);
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new CloneFailedException("Cannot clone Cloneable type "
|
||||
+ obj.getClass().getName(), e);
|
||||
} catch (final InvocationTargetException e) {
|
||||
throw new CloneFailedException("Exception cloning Cloneable type "
|
||||
+ obj.getClass().getName(), e.getCause());
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
final T checked = (T) result;
|
||||
return checked;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Clone an object if possible.</p>
|
||||
*
|
||||
* <p>This method is similar to {@link #clone(Object)}, but will return the provided
|
||||
* instance as the return value instead of {@code null} if the instance
|
||||
* is not cloneable. This is more convenient if the caller uses different
|
||||
* implementations (e.g. of a service) and some of the implementations do not allow concurrent
|
||||
* processing or have state. In such cases the implementation can simply provide a proper
|
||||
* clone implementation and the caller's code does not have to change.</p>
|
||||
*
|
||||
* @param <T> the type of the object
|
||||
* @param obj the object to clone, null returns null
|
||||
* @return the clone if the object implements {@link Cloneable} otherwise the object itself
|
||||
* @throws CloneFailedException if the object is cloneable and the clone operation fails
|
||||
* @since 3.0
|
||||
*/
|
||||
public static <T> T cloneIfPossible(final T obj) {
|
||||
final T clone = clone(obj);
|
||||
return clone == null ? obj : clone;
|
||||
}
|
||||
|
||||
// Null
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Class used as a null placeholder where {@code null}
|
||||
* has another meaning.</p>
|
||||
*
|
||||
* <p>For example, in a {@code HashMap} the
|
||||
* {@link java.util.HashMap#get(java.lang.Object)} method returns
|
||||
* {@code null} if the {@code Map} contains {@code null} or if there is
|
||||
* no matching key. The {@code Null} placeholder can be used to distinguish
|
||||
* between these two cases.</p>
|
||||
*
|
||||
* <p>Another example is {@code Hashtable}, where {@code null}
|
||||
* cannot be stored.</p>
|
||||
*/
|
||||
public static class Null implements Serializable {
|
||||
/**
|
||||
* Required for serialization support. Declare serialization compatibility with Commons Lang 1.0
|
||||
*
|
||||
* @see java.io.Serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 7092611880189329093L;
|
||||
|
||||
/**
|
||||
* Restricted constructor - singleton.
|
||||
*/
|
||||
Null() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Ensure singleton.</p>
|
||||
*
|
||||
* @return the singleton value
|
||||
*/
|
||||
private Object readResolve() {
|
||||
return ObjectUtils.NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
$Id: RELEASE-NOTES.txt 1199820 2011-11-09 16:14:52Z bayard $
|
||||
|
||||
Commons Lang Package
|
||||
Version 3.1
|
||||
Release Notes
|
||||
|
||||
|
||||
INTRODUCTION:
|
||||
|
||||
This document contains the release notes for the 3.1 version of Apache Commons Lang.
|
||||
Commons Lang is a set of utility functions and reusable components that should be of use in any
|
||||
Java environment.
|
||||
|
||||
Lang 3.0 and onwards now targets Java 5.0, making use of features that arrived with Java 5.0 such as generics,
|
||||
variable arguments, autoboxing, concurrency and formatted output.
|
||||
|
||||
For the advice on upgrading from 2.x to 3.x, see the following page:
|
||||
|
||||
http://commons.apache.org/lang/article3_0.html
|
||||
|
||||
CHANGES IN 3.1
|
||||
================
|
||||
|
||||
[LANG-760] Add API StringUtils.toString(byte[] intput, String charsetName)
|
||||
[LANG-756] Add APIs ClassUtils.isPrimitiveWrapper(Class<?>) and isPrimitiveOrWrapper(Class<?>)
|
||||
[LANG-758] Add an example with whitespace in StringUtils.defaultIfEmpty
|
||||
[LANG-752] Fix createLong() so it behaves like createInteger()
|
||||
[LANG-751] Include the actual type in the Validate.isInstance and isAssignableFrom exception messages
|
||||
[LANG-748] Deprecating chomp(String, String)
|
||||
[LANG-736] CharUtils static final array CHAR_STRING is not needed to compute CHAR_STRING_ARRAY
|
||||
[LANG-695] SystemUtils.IS_OS_UNIX doesn't recognize FreeBSD as a Unix system
|
||||
|
||||
BUG FIXES IN 3.1
|
||||
==================
|
||||
|
||||
[LANG-749] Incorrect Bundle-SymbolicName in Manifest
|
||||
[LANG-746] NumberUtils does not handle upper-case hex: 0X and -0X
|
||||
[LANG-744] StringUtils throws java.security.AccessControlException on Google App Engine
|
||||
[LANG-741] Ant build has wrong component.name
|
||||
[LANG-698] Document that the Mutable numbers don't work as expected with String.format
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.builder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The Builder interface is designed to designate a class as a <em>builder</em>
|
||||
* object in the Builder design pattern. Builders are capable of creating and
|
||||
* configuring objects or results that normally take multiple steps to construct
|
||||
* or are very complex to derive.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The builder interface defines a single method, {@link #build()}, that
|
||||
* classes must implement. The result of this method should be the final
|
||||
* configured object or result after all building operations are performed.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It is a recommended practice that the methods supplied to configure the
|
||||
* object or result being built return a reference to {@code this} so that
|
||||
* method calls can be chained together.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Example Builder:
|
||||
* <code><pre>
|
||||
* class FontBuilder implements Builder<Font> {
|
||||
* private Font font;
|
||||
*
|
||||
* public FontBuilder(String fontName) {
|
||||
* this.font = new Font(fontName, Font.PLAIN, 12);
|
||||
* }
|
||||
*
|
||||
* public FontBuilder bold() {
|
||||
* this.font = this.font.deriveFont(Font.BOLD);
|
||||
* return this; // Reference returned so calls can be chained
|
||||
* }
|
||||
*
|
||||
* public FontBuilder size(float pointSize) {
|
||||
* this.font = this.font.deriveFont(pointSize);
|
||||
* return this; // Reference returned so calls can be chained
|
||||
* }
|
||||
*
|
||||
* // Other Font construction methods
|
||||
*
|
||||
* public Font build() {
|
||||
* return this.font;
|
||||
* }
|
||||
* }
|
||||
* </pre></code>
|
||||
*
|
||||
* Example Builder Usage:
|
||||
* <code><pre>
|
||||
* Font bold14ptSansSerifFont = new FontBuilder(Font.SANS_SERIF).bold()
|
||||
* .size(14.0f)
|
||||
* .build();
|
||||
* </pre></code>
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of object that the builder will construct or compute.
|
||||
*
|
||||
* @since 3.0
|
||||
* @version $Id: Builder.java 1088899 2011-04-05 05:31:27Z bayard $
|
||||
*/
|
||||
public interface Builder<T> {
|
||||
|
||||
/**
|
||||
* Returns a reference to the object being constructed or result being
|
||||
* calculated by the builder.
|
||||
*
|
||||
* @return the object constructed or result calculated by the builder.
|
||||
*/
|
||||
public T build();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,945 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.builder;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
import external.org.apache.commons.lang3.ArrayUtils;
|
||||
import external.org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
/**
|
||||
* <p>Assists in implementing {@link Object#equals(Object)} methods.</p>
|
||||
*
|
||||
* <p> This class provides methods to build a good equals method for any
|
||||
* class. It follows rules laid out in
|
||||
* <a href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a>
|
||||
* , by Joshua Bloch. In particular the rule for comparing <code>doubles</code>,
|
||||
* <code>floats</code>, and arrays can be tricky. Also, making sure that
|
||||
* <code>equals()</code> and <code>hashCode()</code> are consistent can be
|
||||
* difficult.</p>
|
||||
*
|
||||
* <p>Two Objects that compare as equals must generate the same hash code,
|
||||
* but two Objects with the same hash code do not have to be equal.</p>
|
||||
*
|
||||
* <p>All relevant fields should be included in the calculation of equals.
|
||||
* Derived fields may be ignored. In particular, any field used in
|
||||
* generating a hash code must be used in the equals method, and vice
|
||||
* versa.</p>
|
||||
*
|
||||
* <p>Typical use for the code is as follows:</p>
|
||||
* <pre>
|
||||
* public boolean equals(Object obj) {
|
||||
* if (obj == null) { return false; }
|
||||
* if (obj == this) { return true; }
|
||||
* if (obj.getClass() != getClass()) {
|
||||
* return false;
|
||||
* }
|
||||
* MyClass rhs = (MyClass) obj;
|
||||
* return new EqualsBuilder()
|
||||
* .appendSuper(super.equals(obj))
|
||||
* .append(field1, rhs.field1)
|
||||
* .append(field2, rhs.field2)
|
||||
* .append(field3, rhs.field3)
|
||||
* .isEquals();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p> Alternatively, there is a method that uses reflection to determine
|
||||
* the fields to test. Because these fields are usually private, the method,
|
||||
* <code>reflectionEquals</code>, uses <code>AccessibleObject.setAccessible</code> to
|
||||
* change the visibility of the fields. This will fail under a security
|
||||
* manager, unless the appropriate permissions are set up correctly. It is
|
||||
* also slower than testing explicitly.</p>
|
||||
*
|
||||
* <p> A typical invocation for this method would look like:</p>
|
||||
* <pre>
|
||||
* public boolean equals(Object obj) {
|
||||
* return EqualsBuilder.reflectionEquals(this, obj);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @since 1.0
|
||||
* @version $Id: EqualsBuilder.java 1091531 2011-04-12 18:29:49Z ggregory $
|
||||
*/
|
||||
public class EqualsBuilder implements Builder<Boolean> {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
private static final ThreadLocal<Set<Pair<IDKey, IDKey>>> REGISTRY = new ThreadLocal<Set<Pair<IDKey, IDKey>>>();
|
||||
|
||||
/*
|
||||
* NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode()
|
||||
* we are in the process of calculating.
|
||||
*
|
||||
* So we generate a one-to-one mapping from the original object to a new object.
|
||||
*
|
||||
* Now HashSet uses equals() to determine if two elements with the same hashcode really
|
||||
* are equal, so we also need to ensure that the replacement objects are only equal
|
||||
* if the original objects are identical.
|
||||
*
|
||||
* The original implementation (2.4 and before) used the System.indentityHashCode()
|
||||
* method - however this is not guaranteed to generate unique ids (e.g. LANG-459)
|
||||
*
|
||||
* We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey)
|
||||
* to disambiguate the duplicate ids.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the registry of object pairs being traversed by the reflection
|
||||
* methods in the current thread.
|
||||
* </p>
|
||||
*
|
||||
* @return Set the registry of objects being traversed
|
||||
* @since 3.0
|
||||
*/
|
||||
static Set<Pair<IDKey, IDKey>> getRegistry() {
|
||||
return REGISTRY.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Converters value pair into a register pair.
|
||||
* </p>
|
||||
*
|
||||
* @param lhs <code>this</code> object
|
||||
* @param rhs the other object
|
||||
*
|
||||
* @return the pair
|
||||
*/
|
||||
static Pair<IDKey, IDKey> getRegisterPair(Object lhs, Object rhs) {
|
||||
IDKey left = new IDKey(lhs);
|
||||
IDKey right = new IDKey(rhs);
|
||||
return Pair.of(left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns <code>true</code> if the registry contains the given object pair.
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
* Objects might be swapped therefore a check is needed if the object pair
|
||||
* is registered in given or swapped order.
|
||||
* </p>
|
||||
*
|
||||
* @param lhs <code>this</code> object to lookup in registry
|
||||
* @param rhs the other object to lookup on registry
|
||||
* @return boolean <code>true</code> if the registry contains the given object.
|
||||
* @since 3.0
|
||||
*/
|
||||
static boolean isRegistered(Object lhs, Object rhs) {
|
||||
Set<Pair<IDKey, IDKey>> registry = getRegistry();
|
||||
Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
|
||||
Pair<IDKey, IDKey> swappedPair = Pair.of(pair.getLeft(), pair.getRight());
|
||||
|
||||
return registry != null
|
||||
&& (registry.contains(pair) || registry.contains(swappedPair));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Registers the given object pair.
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
* </p>
|
||||
*
|
||||
* @param lhs <code>this</code> object to register
|
||||
* @param rhs the other object to register
|
||||
*/
|
||||
static void register(Object lhs, Object rhs) {
|
||||
synchronized (EqualsBuilder.class) {
|
||||
if (getRegistry() == null) {
|
||||
REGISTRY.set(new HashSet<Pair<IDKey, IDKey>>());
|
||||
}
|
||||
}
|
||||
|
||||
Set<Pair<IDKey, IDKey>> registry = getRegistry();
|
||||
Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
|
||||
registry.add(pair);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Unregisters the given object pair.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
*
|
||||
* @param lhs <code>this</code> object to unregister
|
||||
* @param rhs the other object to unregister
|
||||
* @since 3.0
|
||||
*/
|
||||
static void unregister(Object lhs, Object rhs) {
|
||||
Set<Pair<IDKey, IDKey>> registry = getRegistry();
|
||||
if (registry != null) {
|
||||
Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
|
||||
registry.remove(pair);
|
||||
synchronized (EqualsBuilder.class) {
|
||||
//read again
|
||||
registry = getRegistry();
|
||||
if (registry != null && registry.isEmpty()) {
|
||||
REGISTRY.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the fields tested are equals.
|
||||
* The default value is <code>true</code>.
|
||||
*/
|
||||
private boolean isEquals = true;
|
||||
|
||||
/**
|
||||
* <p>Constructor for EqualsBuilder.</p>
|
||||
*
|
||||
* <p>Starts off assuming that equals is <code>true</code>.</p>
|
||||
* @see Object#equals(Object)
|
||||
*/
|
||||
public EqualsBuilder() {
|
||||
// do nothing for now.
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>This method uses reflection to determine if the two <code>Object</code>s
|
||||
* are equal.</p>
|
||||
*
|
||||
* <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
|
||||
* fields. This means that it will throw a security exception if run under
|
||||
* a security manager, if the permissions are not set up correctly. It is also
|
||||
* not as efficient as testing explicitly.</p>
|
||||
*
|
||||
* <p>Transient members will be not be tested, as they are likely derived
|
||||
* fields, and not part of the value of the Object.</p>
|
||||
*
|
||||
* <p>Static fields will not be tested. Superclass fields will be included.</p>
|
||||
*
|
||||
* @param lhs <code>this</code> object
|
||||
* @param rhs the other object
|
||||
* @param excludeFields Collection of String field names to exclude from testing
|
||||
* @return <code>true</code> if the two Objects have tested equals.
|
||||
*/
|
||||
public static boolean reflectionEquals(Object lhs, Object rhs, Collection<String> excludeFields) {
|
||||
return reflectionEquals(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This method uses reflection to determine if the two <code>Object</code>s
|
||||
* are equal.</p>
|
||||
*
|
||||
* <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
|
||||
* fields. This means that it will throw a security exception if run under
|
||||
* a security manager, if the permissions are not set up correctly. It is also
|
||||
* not as efficient as testing explicitly.</p>
|
||||
*
|
||||
* <p>Transient members will be not be tested, as they are likely derived
|
||||
* fields, and not part of the value of the Object.</p>
|
||||
*
|
||||
* <p>Static fields will not be tested. Superclass fields will be included.</p>
|
||||
*
|
||||
* @param lhs <code>this</code> object
|
||||
* @param rhs the other object
|
||||
* @param excludeFields array of field names to exclude from testing
|
||||
* @return <code>true</code> if the two Objects have tested equals.
|
||||
*/
|
||||
public static boolean reflectionEquals(Object lhs, Object rhs, String... excludeFields) {
|
||||
return reflectionEquals(lhs, rhs, false, null, excludeFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This method uses reflection to determine if the two <code>Object</code>s
|
||||
* are equal.</p>
|
||||
*
|
||||
* <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
|
||||
* fields. This means that it will throw a security exception if run under
|
||||
* a security manager, if the permissions are not set up correctly. It is also
|
||||
* not as efficient as testing explicitly.</p>
|
||||
*
|
||||
* <p>If the TestTransients parameter is set to <code>true</code>, transient
|
||||
* members will be tested, otherwise they are ignored, as they are likely
|
||||
* derived fields, and not part of the value of the <code>Object</code>.</p>
|
||||
*
|
||||
* <p>Static fields will not be tested. Superclass fields will be included.</p>
|
||||
*
|
||||
* @param lhs <code>this</code> object
|
||||
* @param rhs the other object
|
||||
* @param testTransients whether to include transient fields
|
||||
* @return <code>true</code> if the two Objects have tested equals.
|
||||
*/
|
||||
public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients) {
|
||||
return reflectionEquals(lhs, rhs, testTransients, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This method uses reflection to determine if the two <code>Object</code>s
|
||||
* are equal.</p>
|
||||
*
|
||||
* <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
|
||||
* fields. This means that it will throw a security exception if run under
|
||||
* a security manager, if the permissions are not set up correctly. It is also
|
||||
* not as efficient as testing explicitly.</p>
|
||||
*
|
||||
* <p>If the testTransients parameter is set to <code>true</code>, transient
|
||||
* members will be tested, otherwise they are ignored, as they are likely
|
||||
* derived fields, and not part of the value of the <code>Object</code>.</p>
|
||||
*
|
||||
* <p>Static fields will not be included. Superclass fields will be appended
|
||||
* up to and including the specified superclass. A null superclass is treated
|
||||
* as java.lang.Object.</p>
|
||||
*
|
||||
* @param lhs <code>this</code> object
|
||||
* @param rhs the other object
|
||||
* @param testTransients whether to include transient fields
|
||||
* @param reflectUpToClass the superclass to reflect up to (inclusive),
|
||||
* may be <code>null</code>
|
||||
* @param excludeFields array of field names to exclude from testing
|
||||
* @return <code>true</code> if the two Objects have tested equals.
|
||||
* @since 2.0
|
||||
*/
|
||||
public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class<?> reflectUpToClass,
|
||||
String... excludeFields) {
|
||||
if (lhs == rhs) {
|
||||
return true;
|
||||
}
|
||||
if (lhs == null || rhs == null) {
|
||||
return false;
|
||||
}
|
||||
// Find the leaf class since there may be transients in the leaf
|
||||
// class or in classes between the leaf and root.
|
||||
// If we are not testing transients or a subclass has no ivars,
|
||||
// then a subclass can test equals to a superclass.
|
||||
Class<?> lhsClass = lhs.getClass();
|
||||
Class<?> rhsClass = rhs.getClass();
|
||||
Class<?> testClass;
|
||||
if (lhsClass.isInstance(rhs)) {
|
||||
testClass = lhsClass;
|
||||
if (!rhsClass.isInstance(lhs)) {
|
||||
// rhsClass is a subclass of lhsClass
|
||||
testClass = rhsClass;
|
||||
}
|
||||
} else if (rhsClass.isInstance(lhs)) {
|
||||
testClass = rhsClass;
|
||||
if (!lhsClass.isInstance(rhs)) {
|
||||
// lhsClass is a subclass of rhsClass
|
||||
testClass = lhsClass;
|
||||
}
|
||||
} else {
|
||||
// The two classes are not related.
|
||||
return false;
|
||||
}
|
||||
EqualsBuilder equalsBuilder = new EqualsBuilder();
|
||||
try {
|
||||
reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields);
|
||||
while (testClass.getSuperclass() != null && testClass != reflectUpToClass) {
|
||||
testClass = testClass.getSuperclass();
|
||||
reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// In this case, we tried to test a subclass vs. a superclass and
|
||||
// the subclass has ivars or the ivars are transient and
|
||||
// we are testing transients.
|
||||
// If a subclass has ivars that we are trying to test them, we get an
|
||||
// exception and we know that the objects are not equal.
|
||||
return false;
|
||||
}
|
||||
return equalsBuilder.isEquals();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Appends the fields and values defined by the given object of the
|
||||
* given Class.</p>
|
||||
*
|
||||
* @param lhs the left hand object
|
||||
* @param rhs the right hand object
|
||||
* @param clazz the class to append details of
|
||||
* @param builder the builder to append to
|
||||
* @param useTransients whether to test transient fields
|
||||
* @param excludeFields array of field names to exclude from testing
|
||||
*/
|
||||
private static void reflectionAppend(
|
||||
Object lhs,
|
||||
Object rhs,
|
||||
Class<?> clazz,
|
||||
EqualsBuilder builder,
|
||||
boolean useTransients,
|
||||
String[] excludeFields) {
|
||||
|
||||
if (isRegistered(lhs, rhs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
register(lhs, rhs);
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
AccessibleObject.setAccessible(fields, true);
|
||||
for (int i = 0; i < fields.length && builder.isEquals; i++) {
|
||||
Field f = fields[i];
|
||||
if (!ArrayUtils.contains(excludeFields, f.getName())
|
||||
&& (f.getName().indexOf('$') == -1)
|
||||
&& (useTransients || !Modifier.isTransient(f.getModifiers()))
|
||||
&& (!Modifier.isStatic(f.getModifiers()))) {
|
||||
try {
|
||||
builder.append(f.get(lhs), f.get(rhs));
|
||||
} catch (IllegalAccessException e) {
|
||||
//this can't happen. Would get a Security exception instead
|
||||
//throw a runtime exception in case the impossible happens.
|
||||
throw new InternalError("Unexpected IllegalAccessException");
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
unregister(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Adds the result of <code>super.equals()</code> to this builder.</p>
|
||||
*
|
||||
* @param superEquals the result of calling <code>super.equals()</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
* @since 2.0
|
||||
*/
|
||||
public EqualsBuilder appendSuper(boolean superEquals) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
isEquals = superEquals;
|
||||
return this;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>Object</code>s are equal using their
|
||||
* <code>equals</code> method.</p>
|
||||
*
|
||||
* @param lhs the left hand object
|
||||
* @param rhs the right hand object
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(Object lhs, Object rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == null || rhs == null) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
Class<?> lhsClass = lhs.getClass();
|
||||
if (!lhsClass.isArray()) {
|
||||
// The simple case, not an array, just test the element
|
||||
isEquals = lhs.equals(rhs);
|
||||
} else if (lhs.getClass() != rhs.getClass()) {
|
||||
// Here when we compare different dimensions, for example: a boolean[][] to a boolean[]
|
||||
this.setEquals(false);
|
||||
}
|
||||
// 'Switch' on type of array, to dispatch to the correct handler
|
||||
// This handles multi dimensional arrays of the same depth
|
||||
else if (lhs instanceof long[]) {
|
||||
append((long[]) lhs, (long[]) rhs);
|
||||
} else if (lhs instanceof int[]) {
|
||||
append((int[]) lhs, (int[]) rhs);
|
||||
} else if (lhs instanceof short[]) {
|
||||
append((short[]) lhs, (short[]) rhs);
|
||||
} else if (lhs instanceof char[]) {
|
||||
append((char[]) lhs, (char[]) rhs);
|
||||
} else if (lhs instanceof byte[]) {
|
||||
append((byte[]) lhs, (byte[]) rhs);
|
||||
} else if (lhs instanceof double[]) {
|
||||
append((double[]) lhs, (double[]) rhs);
|
||||
} else if (lhs instanceof float[]) {
|
||||
append((float[]) lhs, (float[]) rhs);
|
||||
} else if (lhs instanceof boolean[]) {
|
||||
append((boolean[]) lhs, (boolean[]) rhs);
|
||||
} else {
|
||||
// Not an array of primitives
|
||||
append((Object[]) lhs, (Object[]) rhs);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two <code>long</code> s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param lhs
|
||||
* the left hand <code>long</code>
|
||||
* @param rhs
|
||||
* the right hand <code>long</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(long lhs, long rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
isEquals = (lhs == rhs);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>int</code>s are equal.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>int</code>
|
||||
* @param rhs the right hand <code>int</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(int lhs, int rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
isEquals = (lhs == rhs);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>short</code>s are equal.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>short</code>
|
||||
* @param rhs the right hand <code>short</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(short lhs, short rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
isEquals = (lhs == rhs);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>char</code>s are equal.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>char</code>
|
||||
* @param rhs the right hand <code>char</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(char lhs, char rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
isEquals = (lhs == rhs);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>byte</code>s are equal.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>byte</code>
|
||||
* @param rhs the right hand <code>byte</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(byte lhs, byte rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
isEquals = (lhs == rhs);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>double</code>s are equal by testing that the
|
||||
* pattern of bits returned by <code>doubleToLong</code> are equal.</p>
|
||||
*
|
||||
* <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
|
||||
*
|
||||
* <p>It is compatible with the hash code generated by
|
||||
* <code>HashCodeBuilder</code>.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>double</code>
|
||||
* @param rhs the right hand <code>double</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(double lhs, double rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
return append(Double.doubleToLongBits(lhs), Double.doubleToLongBits(rhs));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>float</code>s are equal byt testing that the
|
||||
* pattern of bits returned by doubleToLong are equal.</p>
|
||||
*
|
||||
* <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
|
||||
*
|
||||
* <p>It is compatible with the hash code generated by
|
||||
* <code>HashCodeBuilder</code>.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>float</code>
|
||||
* @param rhs the right hand <code>float</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(float lhs, float rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
return append(Float.floatToIntBits(lhs), Float.floatToIntBits(rhs));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>booleans</code>s are equal.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>boolean</code>
|
||||
* @param rhs the right hand <code>boolean</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(boolean lhs, boolean rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
isEquals = (lhs == rhs);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Performs a deep comparison of two <code>Object</code> arrays.</p>
|
||||
*
|
||||
* <p>This also will be called for the top level of
|
||||
* multi-dimensional, ragged, and multi-typed arrays.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>Object[]</code>
|
||||
* @param rhs the right hand <code>Object[]</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(Object[] lhs, Object[] rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == null || rhs == null) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
if (lhs.length != rhs.length) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
for (int i = 0; i < lhs.length && isEquals; ++i) {
|
||||
append(lhs[i], rhs[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deep comparison of array of <code>long</code>. Length and all
|
||||
* values are compared.</p>
|
||||
*
|
||||
* <p>The method {@link #append(long, long)} is used.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>long[]</code>
|
||||
* @param rhs the right hand <code>long[]</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(long[] lhs, long[] rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == null || rhs == null) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
if (lhs.length != rhs.length) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
for (int i = 0; i < lhs.length && isEquals; ++i) {
|
||||
append(lhs[i], rhs[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deep comparison of array of <code>int</code>. Length and all
|
||||
* values are compared.</p>
|
||||
*
|
||||
* <p>The method {@link #append(int, int)} is used.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>int[]</code>
|
||||
* @param rhs the right hand <code>int[]</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(int[] lhs, int[] rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == null || rhs == null) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
if (lhs.length != rhs.length) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
for (int i = 0; i < lhs.length && isEquals; ++i) {
|
||||
append(lhs[i], rhs[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deep comparison of array of <code>short</code>. Length and all
|
||||
* values are compared.</p>
|
||||
*
|
||||
* <p>The method {@link #append(short, short)} is used.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>short[]</code>
|
||||
* @param rhs the right hand <code>short[]</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(short[] lhs, short[] rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == null || rhs == null) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
if (lhs.length != rhs.length) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
for (int i = 0; i < lhs.length && isEquals; ++i) {
|
||||
append(lhs[i], rhs[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deep comparison of array of <code>char</code>. Length and all
|
||||
* values are compared.</p>
|
||||
*
|
||||
* <p>The method {@link #append(char, char)} is used.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>char[]</code>
|
||||
* @param rhs the right hand <code>char[]</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(char[] lhs, char[] rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == null || rhs == null) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
if (lhs.length != rhs.length) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
for (int i = 0; i < lhs.length && isEquals; ++i) {
|
||||
append(lhs[i], rhs[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deep comparison of array of <code>byte</code>. Length and all
|
||||
* values are compared.</p>
|
||||
*
|
||||
* <p>The method {@link #append(byte, byte)} is used.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>byte[]</code>
|
||||
* @param rhs the right hand <code>byte[]</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(byte[] lhs, byte[] rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == null || rhs == null) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
if (lhs.length != rhs.length) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
for (int i = 0; i < lhs.length && isEquals; ++i) {
|
||||
append(lhs[i], rhs[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deep comparison of array of <code>double</code>. Length and all
|
||||
* values are compared.</p>
|
||||
*
|
||||
* <p>The method {@link #append(double, double)} is used.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>double[]</code>
|
||||
* @param rhs the right hand <code>double[]</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(double[] lhs, double[] rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == null || rhs == null) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
if (lhs.length != rhs.length) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
for (int i = 0; i < lhs.length && isEquals; ++i) {
|
||||
append(lhs[i], rhs[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deep comparison of array of <code>float</code>. Length and all
|
||||
* values are compared.</p>
|
||||
*
|
||||
* <p>The method {@link #append(float, float)} is used.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>float[]</code>
|
||||
* @param rhs the right hand <code>float[]</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(float[] lhs, float[] rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == null || rhs == null) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
if (lhs.length != rhs.length) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
for (int i = 0; i < lhs.length && isEquals; ++i) {
|
||||
append(lhs[i], rhs[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deep comparison of array of <code>boolean</code>. Length and all
|
||||
* values are compared.</p>
|
||||
*
|
||||
* <p>The method {@link #append(boolean, boolean)} is used.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>boolean[]</code>
|
||||
* @param rhs the right hand <code>boolean[]</code>
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(boolean[] lhs, boolean[] rhs) {
|
||||
if (isEquals == false) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == null || rhs == null) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
if (lhs.length != rhs.length) {
|
||||
this.setEquals(false);
|
||||
return this;
|
||||
}
|
||||
for (int i = 0; i < lhs.length && isEquals; ++i) {
|
||||
append(lhs[i], rhs[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns <code>true</code> if the fields that have been checked
|
||||
* are all equal.</p>
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isEquals() {
|
||||
return this.isEquals;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns <code>true</code> if the fields that have been checked
|
||||
* are all equal.</p>
|
||||
*
|
||||
* @return <code>true</code> if all of the fields that have been checked
|
||||
* are equal, <code>false</code> otherwise.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public Boolean build() {
|
||||
return Boolean.valueOf(isEquals());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <code>isEquals</code> value.
|
||||
*
|
||||
* @param isEquals The value to set.
|
||||
* @since 2.1
|
||||
*/
|
||||
protected void setEquals(boolean isEquals) {
|
||||
this.isEquals = isEquals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the EqualsBuilder so you can use the same object again
|
||||
* @since 2.5
|
||||
*/
|
||||
public void reset() {
|
||||
this.isEquals = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,961 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.builder;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import external.org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Assists in implementing {@link Object#hashCode()} methods.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This class enables a good <code>hashCode</code> method to be built for any class. It follows the rules laid out in
|
||||
* the book <a href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a> by Joshua Bloch. Writing a
|
||||
* good <code>hashCode</code> method is actually quite difficult. This class aims to simplify the process.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The following is the approach taken. When appending a data field, the current total is multiplied by the
|
||||
* multiplier then a relevant value
|
||||
* for that data type is added. For example, if the current hashCode is 17, and the multiplier is 37, then
|
||||
* appending the integer 45 will create a hashcode of 674, namely 17 * 37 + 45.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* All relevant fields from the object should be included in the <code>hashCode</code> method. Derived fields may be
|
||||
* excluded. In general, any field used in the <code>equals</code> method must be used in the <code>hashCode</code>
|
||||
* method.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* To use this class write code as follows:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* public class Person {
|
||||
* String name;
|
||||
* int age;
|
||||
* boolean smoker;
|
||||
* ...
|
||||
*
|
||||
* public int hashCode() {
|
||||
* // you pick a hard-coded, randomly chosen, non-zero, odd number
|
||||
* // ideally different for each class
|
||||
* return new HashCodeBuilder(17, 37).
|
||||
* append(name).
|
||||
* append(age).
|
||||
* append(smoker).
|
||||
* toHashCode();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* If required, the superclass <code>hashCode()</code> can be added using {@link #appendSuper}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Alternatively, there is a method that uses reflection to determine the fields to test. Because these fields are
|
||||
* usually private, the method, <code>reflectionHashCode</code>, uses <code>AccessibleObject.setAccessible</code>
|
||||
* to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions
|
||||
* are set up correctly. It is also slower than testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* A typical invocation for this method would look like:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* public int hashCode() {
|
||||
* return HashCodeBuilder.reflectionHashCode(this);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @since 1.0
|
||||
* @version $Id: HashCodeBuilder.java 1144929 2011-07-10 18:26:16Z ggregory $
|
||||
*/
|
||||
public class HashCodeBuilder implements Builder<Integer> {
|
||||
/**
|
||||
* <p>
|
||||
* A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops.
|
||||
* </p>
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
private static final ThreadLocal<Set<IDKey>> REGISTRY = new ThreadLocal<Set<IDKey>>();
|
||||
|
||||
/*
|
||||
* NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode()
|
||||
* we are in the process of calculating.
|
||||
*
|
||||
* So we generate a one-to-one mapping from the original object to a new object.
|
||||
*
|
||||
* Now HashSet uses equals() to determine if two elements with the same hashcode really
|
||||
* are equal, so we also need to ensure that the replacement objects are only equal
|
||||
* if the original objects are identical.
|
||||
*
|
||||
* The original implementation (2.4 and before) used the System.indentityHashCode()
|
||||
* method - however this is not guaranteed to generate unique ids (e.g. LANG-459)
|
||||
*
|
||||
* We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey)
|
||||
* to disambiguate the duplicate ids.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the registry of objects being traversed by the reflection methods in the current thread.
|
||||
* </p>
|
||||
*
|
||||
* @return Set the registry of objects being traversed
|
||||
* @since 2.3
|
||||
*/
|
||||
static Set<IDKey> getRegistry() {
|
||||
return REGISTRY.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns <code>true</code> if the registry contains the given object. Used by the reflection methods to avoid
|
||||
* infinite loops.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* The object to lookup in the registry.
|
||||
* @return boolean <code>true</code> if the registry contains the given object.
|
||||
* @since 2.3
|
||||
*/
|
||||
static boolean isRegistered(Object value) {
|
||||
Set<IDKey> registry = getRegistry();
|
||||
return registry != null && registry.contains(new IDKey(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Appends the fields and values defined by the given object of the given <code>Class</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the object to append details of
|
||||
* @param clazz
|
||||
* the class to append details of
|
||||
* @param builder
|
||||
* the builder to append to
|
||||
* @param useTransients
|
||||
* whether to use transient fields
|
||||
* @param excludeFields
|
||||
* Collection of String field names to exclude from use in calculation of hash code
|
||||
*/
|
||||
private static void reflectionAppend(Object object, Class<?> clazz, HashCodeBuilder builder, boolean useTransients,
|
||||
String[] excludeFields) {
|
||||
if (isRegistered(object)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
register(object);
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
AccessibleObject.setAccessible(fields, true);
|
||||
for (Field field : fields) {
|
||||
if (!ArrayUtils.contains(excludeFields, field.getName())
|
||||
&& (field.getName().indexOf('$') == -1)
|
||||
&& (useTransients || !Modifier.isTransient(field.getModifiers()))
|
||||
&& (!Modifier.isStatic(field.getModifiers()))) {
|
||||
try {
|
||||
Object fieldValue = field.get(object);
|
||||
builder.append(fieldValue);
|
||||
} catch (IllegalAccessException e) {
|
||||
// this can't happen. Would get a Security exception instead
|
||||
// throw a runtime exception in case the impossible happens.
|
||||
throw new InternalError("Unexpected IllegalAccessException");
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
unregister(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This method uses reflection to build a valid hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||
* <code>Object</code>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be tested. Superclass fields will be included.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||
* </p>
|
||||
*
|
||||
* @param initialNonZeroOddNumber
|
||||
* a non-zero, odd number used as the initial value
|
||||
* @param multiplierNonZeroOddNumber
|
||||
* a non-zero, odd number used as the multiplier
|
||||
* @param object
|
||||
* the Object to create a <code>hashCode</code> for
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is <code>null</code>
|
||||
* @throws IllegalArgumentException
|
||||
* if the number is zero or even
|
||||
*/
|
||||
public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object) {
|
||||
return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This method uses reflection to build a valid hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be tested. Superclass fields will be included.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||
* </p>
|
||||
*
|
||||
* @param initialNonZeroOddNumber
|
||||
* a non-zero, odd number used as the initial value
|
||||
* @param multiplierNonZeroOddNumber
|
||||
* a non-zero, odd number used as the multiplier
|
||||
* @param object
|
||||
* the Object to create a <code>hashCode</code> for
|
||||
* @param testTransients
|
||||
* whether to include transient fields
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is <code>null</code>
|
||||
* @throws IllegalArgumentException
|
||||
* if the number is zero or even
|
||||
*/
|
||||
public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object,
|
||||
boolean testTransients) {
|
||||
return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This method uses reflection to build a valid hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be included. Superclass fields will be included up to and including the specified
|
||||
* superclass. A null superclass is treated as java.lang.Object.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||
* </p>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the object involved
|
||||
* @param initialNonZeroOddNumber
|
||||
* a non-zero, odd number used as the initial value
|
||||
* @param multiplierNonZeroOddNumber
|
||||
* a non-zero, odd number used as the multiplier
|
||||
* @param object
|
||||
* the Object to create a <code>hashCode</code> for
|
||||
* @param testTransients
|
||||
* whether to include transient fields
|
||||
* @param reflectUpToClass
|
||||
* the superclass to reflect up to (inclusive), may be <code>null</code>
|
||||
* @param excludeFields
|
||||
* array of field names to exclude from use in calculation of hash code
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is <code>null</code>
|
||||
* @throws IllegalArgumentException
|
||||
* if the number is zero or even
|
||||
* @since 2.0
|
||||
*/
|
||||
public static <T> int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, T object,
|
||||
boolean testTransients, Class<? super T> reflectUpToClass, String... excludeFields) {
|
||||
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException("The object to build a hash code for must not be null");
|
||||
}
|
||||
HashCodeBuilder builder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber);
|
||||
Class<?> clazz = object.getClass();
|
||||
reflectionAppend(object, clazz, builder, testTransients, excludeFields);
|
||||
while (clazz.getSuperclass() != null && clazz != reflectUpToClass) {
|
||||
clazz = clazz.getSuperclass();
|
||||
reflectionAppend(object, clazz, builder, testTransients, excludeFields);
|
||||
}
|
||||
return builder.toHashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This method uses reflection to build a valid hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be tested. Superclass fields will be included.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to create a <code>hashCode</code> for
|
||||
* @param testTransients
|
||||
* whether to include transient fields
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the object is <code>null</code>
|
||||
*/
|
||||
public static int reflectionHashCode(Object object, boolean testTransients) {
|
||||
return reflectionHashCode(17, 37, object, testTransients, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This method uses reflection to build a valid hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||
* <code>Object</code>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be tested. Superclass fields will be included.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to create a <code>hashCode</code> for
|
||||
* @param excludeFields
|
||||
* Collection of String field names to exclude from use in calculation of hash code
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the object is <code>null</code>
|
||||
*/
|
||||
public static int reflectionHashCode(Object object, Collection<String> excludeFields) {
|
||||
return reflectionHashCode(object, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This method uses reflection to build a valid hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||
* <code>Object</code>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be tested. Superclass fields will be included.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to create a <code>hashCode</code> for
|
||||
* @param excludeFields
|
||||
* array of field names to exclude from use in calculation of hash code
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the object is <code>null</code>
|
||||
*/
|
||||
public static int reflectionHashCode(Object object, String... excludeFields) {
|
||||
return reflectionHashCode(17, 37, object, false, null, excludeFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Registers the given object. Used by the reflection methods to avoid infinite loops.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* The object to register.
|
||||
*/
|
||||
static void register(Object value) {
|
||||
synchronized (HashCodeBuilder.class) {
|
||||
if (getRegistry() == null) {
|
||||
REGISTRY.set(new HashSet<IDKey>());
|
||||
}
|
||||
}
|
||||
getRegistry().add(new IDKey(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Unregisters the given object.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
*
|
||||
* @param value
|
||||
* The object to unregister.
|
||||
* @since 2.3
|
||||
*/
|
||||
static void unregister(Object value) {
|
||||
Set<IDKey> registry = getRegistry();
|
||||
if (registry != null) {
|
||||
registry.remove(new IDKey(value));
|
||||
synchronized (HashCodeBuilder.class) {
|
||||
//read again
|
||||
registry = getRegistry();
|
||||
if (registry != null && registry.isEmpty()) {
|
||||
REGISTRY.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant to use in building the hashCode.
|
||||
*/
|
||||
private final int iConstant;
|
||||
|
||||
/**
|
||||
* Running total of the hashCode.
|
||||
*/
|
||||
private int iTotal = 0;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Uses two hard coded choices for the constants needed to build a <code>hashCode</code>.
|
||||
* </p>
|
||||
*/
|
||||
public HashCodeBuilder() {
|
||||
iConstant = 37;
|
||||
iTotal = 17;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||
* however this is not vital.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Prime numbers are preferred, especially for the multiplier.
|
||||
* </p>
|
||||
*
|
||||
* @param initialNonZeroOddNumber
|
||||
* a non-zero, odd number used as the initial value
|
||||
* @param multiplierNonZeroOddNumber
|
||||
* a non-zero, odd number used as the multiplier
|
||||
* @throws IllegalArgumentException
|
||||
* if the number is zero or even
|
||||
*/
|
||||
public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) {
|
||||
if (initialNonZeroOddNumber == 0) {
|
||||
throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value");
|
||||
}
|
||||
if (initialNonZeroOddNumber % 2 == 0) {
|
||||
throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value");
|
||||
}
|
||||
if (multiplierNonZeroOddNumber == 0) {
|
||||
throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier");
|
||||
}
|
||||
if (multiplierNonZeroOddNumber % 2 == 0) {
|
||||
throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier");
|
||||
}
|
||||
iConstant = multiplierNonZeroOddNumber;
|
||||
iTotal = initialNonZeroOddNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>boolean</code>.
|
||||
* </p>
|
||||
* <p>
|
||||
* This adds <code>1</code> when true, and <code>0</code> when false to the <code>hashCode</code>.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is in contrast to the standard <code>java.lang.Boolean.hashCode</code> handling, which computes
|
||||
* a <code>hashCode</code> value of <code>1231</code> for <code>java.lang.Boolean</code> instances
|
||||
* that represent <code>true</code> or <code>1237</code> for <code>java.lang.Boolean</code> instances
|
||||
* that represent <code>false</code>.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is in accordance with the <quote>Effective Java</quote> design.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the boolean to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(boolean value) {
|
||||
iTotal = iTotal * iConstant + (value ? 0 : 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>boolean</code> array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(boolean[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (boolean element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>byte</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the byte to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(byte value) {
|
||||
iTotal = iTotal * iConstant + value;
|
||||
return this;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>byte</code> array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(byte[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (byte element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>char</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the char to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(char value) {
|
||||
iTotal = iTotal * iConstant + value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>char</code> array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(char[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (char element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>double</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the double to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(double value) {
|
||||
return append(Double.doubleToLongBits(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>double</code> array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(double[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (double element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>float</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the float to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(float value) {
|
||||
iTotal = iTotal * iConstant + Float.floatToIntBits(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>float</code> array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(float[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (float element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for an <code>int</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the int to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(int value) {
|
||||
iTotal = iTotal * iConstant + value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for an <code>int</code> array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(int[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (int element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>long</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the long to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
// NOTE: This method uses >> and not >>> as Effective Java and
|
||||
// Long.hashCode do. Ideally we should switch to >>> at
|
||||
// some stage. There are backwards compat issues, so
|
||||
// that will have to wait for the time being. cf LANG-342.
|
||||
public HashCodeBuilder append(long value) {
|
||||
iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>long</code> array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(long[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (long element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for an <code>Object</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(Object object) {
|
||||
if (object == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
|
||||
} else {
|
||||
if(object.getClass().isArray()) {
|
||||
// 'Switch' on type of array, to dispatch to the correct handler
|
||||
// This handles multi dimensional arrays
|
||||
if (object instanceof long[]) {
|
||||
append((long[]) object);
|
||||
} else if (object instanceof int[]) {
|
||||
append((int[]) object);
|
||||
} else if (object instanceof short[]) {
|
||||
append((short[]) object);
|
||||
} else if (object instanceof char[]) {
|
||||
append((char[]) object);
|
||||
} else if (object instanceof byte[]) {
|
||||
append((byte[]) object);
|
||||
} else if (object instanceof double[]) {
|
||||
append((double[]) object);
|
||||
} else if (object instanceof float[]) {
|
||||
append((float[]) object);
|
||||
} else if (object instanceof boolean[]) {
|
||||
append((boolean[]) object);
|
||||
} else {
|
||||
// Not an array of primitives
|
||||
append((Object[]) object);
|
||||
}
|
||||
} else {
|
||||
iTotal = iTotal * iConstant + object.hashCode();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for an <code>Object</code> array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(Object[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (Object element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>short</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the short to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(short value) {
|
||||
iTotal = iTotal * iConstant + value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a <code>hashCode</code> for a <code>short</code> array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the <code>hashCode</code>
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(short[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (short element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Adds the result of super.hashCode() to this builder.
|
||||
* </p>
|
||||
*
|
||||
* @param superHashCode
|
||||
* the result of calling <code>super.hashCode()</code>
|
||||
* @return this HashCodeBuilder, used to chain calls.
|
||||
* @since 2.0
|
||||
*/
|
||||
public HashCodeBuilder appendSuper(int superHashCode) {
|
||||
iTotal = iTotal * iConstant + superHashCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Return the computed <code>hashCode</code>.
|
||||
* </p>
|
||||
*
|
||||
* @return <code>hashCode</code> based on the fields appended
|
||||
*/
|
||||
public int toHashCode() {
|
||||
return iTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the computed <code>hashCode</code>.
|
||||
*
|
||||
* @return <code>hashCode</code> based on the fields appended
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public Integer build() {
|
||||
return Integer.valueOf(toHashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The computed <code>hashCode</code> from toHashCode() is returned due to the likelihood
|
||||
* of bugs in mis-calling toHashCode() and the unlikeliness of it mattering what the hashCode for
|
||||
* HashCodeBuilder itself is.</p>
|
||||
*
|
||||
* @return <code>hashCode</code> based on the fields appended
|
||||
* @since 2.5
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toHashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.builder;
|
||||
|
||||
// adapted from org.apache.axis.utils.IDKey
|
||||
|
||||
/**
|
||||
* Wrap an identity key (System.identityHashCode())
|
||||
* so that an object can only be equal() to itself.
|
||||
*
|
||||
* This is necessary to disambiguate the occasional duplicate
|
||||
* identityHashCodes that can occur.
|
||||
*
|
||||
*/
|
||||
final class IDKey {
|
||||
private final Object value;
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* Constructor for IDKey
|
||||
* @param _value The value
|
||||
*/
|
||||
public IDKey(Object _value) {
|
||||
// This is the Object hashcode
|
||||
id = System.identityHashCode(_value);
|
||||
// There have been some cases (LANG-459) that return the
|
||||
// same identity hash code for different objects. So
|
||||
// the value is also added to disambiguate these cases.
|
||||
value = _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns hashcode - i.e. the system identity hashcode.
|
||||
* @return the hashcode
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if instances are equal
|
||||
* @param other The other object to compare to
|
||||
* @return if the instances are for the same object
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof IDKey)) {
|
||||
return false;
|
||||
}
|
||||
IDKey idKey = (IDKey) other;
|
||||
if (id != idKey.id) {
|
||||
return false;
|
||||
}
|
||||
// Note that identity equals is used.
|
||||
return value == idKey.value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,691 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.builder;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import external.org.apache.commons.lang3.ArrayUtils;
|
||||
import external.org.apache.commons.lang3.ClassUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Assists in implementing {@link Object#toString()} methods using reflection.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class uses reflection to determine the fields to append. Because these fields are usually private, the class
|
||||
* uses {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} to
|
||||
* change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions are
|
||||
* set up correctly.
|
||||
* </p>
|
||||
* <p>
|
||||
* Using reflection to access (private) fields circumvents any synchronization protection guarding access to these
|
||||
* fields. If a toString method cannot safely read a field, you should exclude it from the toString method, or use
|
||||
* synchronization consistent with the class' lock management around the invocation of the method. Take special care to
|
||||
* exclude non-thread-safe collection classes, because these classes may throw ConcurrentModificationException if
|
||||
* modified while the toString method is executing.
|
||||
* </p>
|
||||
* <p>
|
||||
* A typical invocation for this method would look like:
|
||||
* </p>
|
||||
* <pre>
|
||||
* public String toString() {
|
||||
* return ReflectionToStringBuilder.toString(this);
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* You can also use the builder to debug 3rd party objects:
|
||||
* </p>
|
||||
* <pre>
|
||||
* System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject));
|
||||
* </pre>
|
||||
* <p>
|
||||
* A subclass can control field output by overriding the methods:
|
||||
* <ul>
|
||||
* <li>{@link #accept(java.lang.reflect.Field)}</li>
|
||||
* <li>{@link #getValue(java.lang.reflect.Field)}</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* For example, this method does <i>not</i> include the <code>password</code> field in the returned <code>String</code>:
|
||||
* </p>
|
||||
* <pre>
|
||||
* public String toString() {
|
||||
* return (new ReflectionToStringBuilder(this) {
|
||||
* protected boolean accept(Field f) {
|
||||
* return super.accept(f) && !f.getName().equals("password");
|
||||
* }
|
||||
* }).toString();
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* The exact format of the <code>toString</code> is determined by the {@link ToStringStyle} passed into the constructor.
|
||||
* </p>
|
||||
*
|
||||
* @since 2.0
|
||||
* @version $Id: ReflectionToStringBuilder.java 1200177 2011-11-10 06:14:33Z ggregory $
|
||||
*/
|
||||
public class ReflectionToStringBuilder extends ToStringBuilder {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a <code>toString</code> value using the default <code>ToStringStyle</code> through reflection.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Transient members will be not be included, as they are likely derived. Static fields will not be included.
|
||||
* Superclass fields will be appended.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to be output
|
||||
* @return the String result
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is <code>null</code>
|
||||
*/
|
||||
public static String toString(Object object) {
|
||||
return toString(object, null, false, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a <code>toString</code> value through reflection.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Transient members will be not be included, as they are likely derived. Static fields will not be included.
|
||||
* Superclass fields will be appended.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to be output
|
||||
* @param style
|
||||
* the style of the <code>toString</code> to create, may be <code>null</code>
|
||||
* @return the String result
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object or <code>ToStringStyle</code> is <code>null</code>
|
||||
*/
|
||||
public static String toString(Object object, ToStringStyle style) {
|
||||
return toString(object, style, false, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a <code>toString</code> value through reflection.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the <code>outputTransients</code> is <code>true</code>, transient members will be output, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the Object.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be included. Superclass fields will be appended.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to be output
|
||||
* @param style
|
||||
* the style of the <code>toString</code> to create, may be <code>null</code>
|
||||
* @param outputTransients
|
||||
* whether to include transient fields
|
||||
* @return the String result
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is <code>null</code>
|
||||
*/
|
||||
public static String toString(Object object, ToStringStyle style, boolean outputTransients) {
|
||||
return toString(object, style, outputTransients, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a <code>toString</code> value through reflection.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the <code>outputTransients</code> is <code>true</code>, transient fields will be output, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the Object.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the <code>outputStatics</code> is <code>true</code>, static fields will be output, otherwise they are
|
||||
* ignored.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be included. Superclass fields will be appended.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to be output
|
||||
* @param style
|
||||
* the style of the <code>toString</code> to create, may be <code>null</code>
|
||||
* @param outputTransients
|
||||
* whether to include transient fields
|
||||
* @param outputStatics
|
||||
* whether to include transient fields
|
||||
* @return the String result
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is <code>null</code>
|
||||
* @since 2.1
|
||||
*/
|
||||
public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics) {
|
||||
return toString(object, style, outputTransients, outputStatics, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a <code>toString</code> value through reflection.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the <code>outputTransients</code> is <code>true</code>, transient fields will be output, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the Object.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the <code>outputStatics</code> is <code>true</code>, static fields will be output, otherwise they are
|
||||
* ignored.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as
|
||||
* <code>java.lang.Object</code>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
|
||||
* </p>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the object
|
||||
* @param object
|
||||
* the Object to be output
|
||||
* @param style
|
||||
* the style of the <code>toString</code> to create, may be <code>null</code>
|
||||
* @param outputTransients
|
||||
* whether to include transient fields
|
||||
* @param outputStatics
|
||||
* whether to include static fields
|
||||
* @param reflectUpToClass
|
||||
* the superclass to reflect up to (inclusive), may be <code>null</code>
|
||||
* @return the String result
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is <code>null</code>
|
||||
* @since 2.1
|
||||
*/
|
||||
public static <T> String toString(
|
||||
T object, ToStringStyle style, boolean outputTransients,
|
||||
boolean outputStatics, Class<? super T> reflectUpToClass) {
|
||||
return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics)
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a String for a toString method excluding the given field names.
|
||||
*
|
||||
* @param object
|
||||
* The object to "toString".
|
||||
* @param excludeFieldNames
|
||||
* The field names to exclude. Null excludes nothing.
|
||||
* @return The toString value.
|
||||
*/
|
||||
public static String toStringExclude(Object object, Collection<String> excludeFieldNames) {
|
||||
return toStringExclude(object, toNoNullStringArray(excludeFieldNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given Collection into an array of Strings. The returned array does not contain <code>null</code>
|
||||
* entries. Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} if an array element
|
||||
* is <code>null</code>.
|
||||
*
|
||||
* @param collection
|
||||
* The collection to convert
|
||||
* @return A new array of Strings.
|
||||
*/
|
||||
static String[] toNoNullStringArray(Collection<String> collection) {
|
||||
if (collection == null) {
|
||||
return ArrayUtils.EMPTY_STRING_ARRAY;
|
||||
}
|
||||
return toNoNullStringArray(collection.toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new array of Strings without null elements. Internal method used to normalize exclude lists
|
||||
* (arrays and collections). Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException}
|
||||
* if an array element is <code>null</code>.
|
||||
*
|
||||
* @param array
|
||||
* The array to check
|
||||
* @return The given array or a new array without null.
|
||||
*/
|
||||
static String[] toNoNullStringArray(Object[] array) {
|
||||
List<String> list = new ArrayList<String>(array.length);
|
||||
for (Object e : array) {
|
||||
if (e != null) {
|
||||
list.add(e.toString());
|
||||
}
|
||||
}
|
||||
return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds a String for a toString method excluding the given field names.
|
||||
*
|
||||
* @param object
|
||||
* The object to "toString".
|
||||
* @param excludeFieldNames
|
||||
* The field names to exclude
|
||||
* @return The toString value.
|
||||
*/
|
||||
public static String toStringExclude(Object object, String... excludeFieldNames) {
|
||||
return new ReflectionToStringBuilder(object).setExcludeFieldNames(excludeFieldNames).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not to append static fields.
|
||||
*/
|
||||
private boolean appendStatics = false;
|
||||
|
||||
/**
|
||||
* Whether or not to append transient fields.
|
||||
*/
|
||||
private boolean appendTransients = false;
|
||||
|
||||
/**
|
||||
* Which field names to exclude from output. Intended for fields like <code>"password"</code>.
|
||||
*
|
||||
* @since 3.0 this is protected instead of private
|
||||
*/
|
||||
protected String[] excludeFieldNames;
|
||||
|
||||
/**
|
||||
* The last super class to stop appending fields for.
|
||||
*/
|
||||
private Class<?> upToClass = null;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This constructor outputs using the default style set with <code>setDefaultStyle</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to build a <code>toString</code> for, must not be <code>null</code>
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object passed in is <code>null</code>
|
||||
*/
|
||||
public ReflectionToStringBuilder(Object object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is <code>null</code>, the default style is used.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to build a <code>toString</code> for, must not be <code>null</code>
|
||||
* @param style
|
||||
* the style of the <code>toString</code> to create, may be <code>null</code>
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object passed in is <code>null</code>
|
||||
*/
|
||||
public ReflectionToStringBuilder(Object object, ToStringStyle style) {
|
||||
super(object, style);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is <code>null</code>, the default style is used.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the buffer is <code>null</code>, a new one is created.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to build a <code>toString</code> for
|
||||
* @param style
|
||||
* the style of the <code>toString</code> to create, may be <code>null</code>
|
||||
* @param buffer
|
||||
* the <code>StringBuffer</code> to populate, may be <code>null</code>
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object passed in is <code>null</code>
|
||||
*/
|
||||
public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) {
|
||||
super(object, style, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the object
|
||||
* @param object
|
||||
* the Object to build a <code>toString</code> for
|
||||
* @param style
|
||||
* the style of the <code>toString</code> to create, may be <code>null</code>
|
||||
* @param buffer
|
||||
* the <code>StringBuffer</code> to populate, may be <code>null</code>
|
||||
* @param reflectUpToClass
|
||||
* the superclass to reflect up to (inclusive), may be <code>null</code>
|
||||
* @param outputTransients
|
||||
* whether to include transient fields
|
||||
* @param outputStatics
|
||||
* whether to include static fields
|
||||
* @since 2.1
|
||||
*/
|
||||
public <T> ReflectionToStringBuilder(
|
||||
T object, ToStringStyle style, StringBuffer buffer,
|
||||
Class<? super T> reflectUpToClass, boolean outputTransients, boolean outputStatics) {
|
||||
super(object, style, buffer);
|
||||
this.setUpToClass(reflectUpToClass);
|
||||
this.setAppendTransients(outputTransients);
|
||||
this.setAppendStatics(outputStatics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not to append the given <code>Field</code>.
|
||||
* <ul>
|
||||
* <li>Transient fields are appended only if {@link #isAppendTransients()} returns <code>true</code>.
|
||||
* <li>Static fields are appended only if {@link #isAppendStatics()} returns <code>true</code>.
|
||||
* <li>Inner class fields are not appened.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param field
|
||||
* The Field to test.
|
||||
* @return Whether or not to append the given <code>Field</code>.
|
||||
*/
|
||||
protected boolean accept(Field field) {
|
||||
if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) {
|
||||
// Reject field from inner class.
|
||||
return false;
|
||||
}
|
||||
if (Modifier.isTransient(field.getModifiers()) && !this.isAppendTransients()) {
|
||||
// Reject transient fields.
|
||||
return false;
|
||||
}
|
||||
if (Modifier.isStatic(field.getModifiers()) && !this.isAppendStatics()) {
|
||||
// Reject static fields.
|
||||
return false;
|
||||
}
|
||||
if (this.excludeFieldNames != null
|
||||
&& Arrays.binarySearch(this.excludeFieldNames, field.getName()) >= 0) {
|
||||
// Reject fields from the getExcludeFieldNames list.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Appends the fields and values defined by the given object of the given Class.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a cycle is detected as an object is "toString()'ed", such an object is rendered as if
|
||||
* <code>Object.toString()</code> had been called and not implemented by the object.
|
||||
* </p>
|
||||
*
|
||||
* @param clazz
|
||||
* The class of object parameter
|
||||
*/
|
||||
protected void appendFieldsIn(Class<?> clazz) {
|
||||
if (clazz.isArray()) {
|
||||
this.reflectionAppendArray(this.getObject());
|
||||
return;
|
||||
}
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
AccessibleObject.setAccessible(fields, true);
|
||||
for (Field field : fields) {
|
||||
String fieldName = field.getName();
|
||||
if (this.accept(field)) {
|
||||
try {
|
||||
// Warning: Field.get(Object) creates wrappers objects
|
||||
// for primitive types.
|
||||
Object fieldValue = this.getValue(field);
|
||||
this.append(fieldName, fieldValue);
|
||||
} catch (IllegalAccessException ex) {
|
||||
//this can't happen. Would get a Security exception
|
||||
// instead
|
||||
//throw a runtime exception in case the impossible
|
||||
// happens.
|
||||
throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the excludeFieldNames.
|
||||
*/
|
||||
public String[] getExcludeFieldNames() {
|
||||
return this.excludeFieldNames.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets the last super class to stop appending fields for.
|
||||
* </p>
|
||||
*
|
||||
* @return The last super class to stop appending fields for.
|
||||
*/
|
||||
public Class<?> getUpToClass() {
|
||||
return this.upToClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Calls <code>java.lang.reflect.Field.get(Object)</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param field
|
||||
* The Field to query.
|
||||
* @return The Object from the given Field.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* see {@link java.lang.reflect.Field#get(Object)}
|
||||
* @throws IllegalAccessException
|
||||
* see {@link java.lang.reflect.Field#get(Object)}
|
||||
*
|
||||
* @see java.lang.reflect.Field#get(Object)
|
||||
*/
|
||||
protected Object getValue(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||
return field.get(this.getObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets whether or not to append static fields.
|
||||
* </p>
|
||||
*
|
||||
* @return Whether or not to append static fields.
|
||||
* @since 2.1
|
||||
*/
|
||||
public boolean isAppendStatics() {
|
||||
return this.appendStatics;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets whether or not to append transient fields.
|
||||
* </p>
|
||||
*
|
||||
* @return Whether or not to append transient fields.
|
||||
*/
|
||||
public boolean isAppendTransients() {
|
||||
return this.appendTransients;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append to the <code>toString</code> an <code>Object</code> array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the <code>toString</code>
|
||||
* @return this
|
||||
*/
|
||||
public ReflectionToStringBuilder reflectionAppendArray(Object array) {
|
||||
this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets whether or not to append static fields.
|
||||
* </p>
|
||||
*
|
||||
* @param appendStatics
|
||||
* Whether or not to append static fields.
|
||||
* @since 2.1
|
||||
*/
|
||||
public void setAppendStatics(boolean appendStatics) {
|
||||
this.appendStatics = appendStatics;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets whether or not to append transient fields.
|
||||
* </p>
|
||||
*
|
||||
* @param appendTransients
|
||||
* Whether or not to append transient fields.
|
||||
*/
|
||||
public void setAppendTransients(boolean appendTransients) {
|
||||
this.appendTransients = appendTransients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field names to exclude.
|
||||
*
|
||||
* @param excludeFieldNamesParam
|
||||
* The excludeFieldNames to excluding from toString or <code>null</code>.
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public ReflectionToStringBuilder setExcludeFieldNames(String... excludeFieldNamesParam) {
|
||||
if (excludeFieldNamesParam == null) {
|
||||
this.excludeFieldNames = null;
|
||||
} else {
|
||||
//clone and remove nulls
|
||||
this.excludeFieldNames = toNoNullStringArray(excludeFieldNamesParam);
|
||||
Arrays.sort(this.excludeFieldNames);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the last super class to stop appending fields for.
|
||||
* </p>
|
||||
*
|
||||
* @param clazz
|
||||
* The last super class to stop appending fields for.
|
||||
*/
|
||||
public void setUpToClass(Class<?> clazz) {
|
||||
if (clazz != null) {
|
||||
Object object = getObject();
|
||||
if (object != null && clazz.isInstance(object) == false) {
|
||||
throw new IllegalArgumentException("Specified class is not a superclass of the object");
|
||||
}
|
||||
}
|
||||
this.upToClass = clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets the String built by this builder.
|
||||
* </p>
|
||||
*
|
||||
* @return the built string
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this.getObject() == null) {
|
||||
return this.getStyle().getNullText();
|
||||
}
|
||||
Class<?> clazz = this.getObject().getClass();
|
||||
this.appendFieldsIn(clazz);
|
||||
while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) {
|
||||
clazz = clazz.getSuperclass();
|
||||
this.appendFieldsIn(clazz);
|
||||
}
|
||||
return super.toString();
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,28 +0,0 @@
|
|||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
Assists in creating consistent <code>equals(Object)</code>, <code>toString()</code>,
|
||||
<code>hashCode()</code>, and <code>compareTo(Object)</code> methods.
|
||||
@see java.lang.Object#equals(Object)
|
||||
@see java.lang.Object#toString()
|
||||
@see java.lang.Object#hashCode()
|
||||
@see java.lang.Comparable#compareTo(Object)
|
||||
@since 1.0
|
||||
<p>These classes are not thread-safe.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when a clone cannot be created. In contrast to
|
||||
* {@link CloneNotSupportedException} this is a {@link RuntimeException}.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class CloneFailedException extends RuntimeException {
|
||||
// ~ Static fields/initializers ---------------------------------------------
|
||||
|
||||
private static final long serialVersionUID = 20091223L;
|
||||
|
||||
// ~ Constructors -----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs a CloneFailedException.
|
||||
*
|
||||
* @param message description of the exception
|
||||
* @since upcoming
|
||||
*/
|
||||
public CloneFailedException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a CloneFailedException.
|
||||
*
|
||||
* @param cause cause of the exception
|
||||
* @since upcoming
|
||||
*/
|
||||
public CloneFailedException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a CloneFailedException.
|
||||
*
|
||||
* @param message description of the exception
|
||||
* @param cause cause of the exception
|
||||
* @since upcoming
|
||||
*/
|
||||
public CloneFailedException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
Provides functionality for Exceptions.
|
||||
<p>Contains the concept of an exception with context i.e. such an exception
|
||||
will contain a map with keys and values. This provides an easy way to pass valuable
|
||||
state information at exception time in useful form to a calling process.</p>
|
||||
<p>Lastly, {@link org.apache.commons.lang3.exception.ExceptionUtils}
|
||||
also contains <code>Throwable</code> manipulation and examination routines.</p>
|
||||
@since 1.0
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.mutable;
|
||||
|
||||
/**
|
||||
* Provides mutable access to a value.
|
||||
* <p>
|
||||
* <code>Mutable</code> is used as a generic interface to the implementations in this package.
|
||||
* <p>
|
||||
* A typical use case would be to enable a primitive or string to be passed to a method and allow that method to
|
||||
* effectively change the value of the primitive/string. Another use case is to store a frequently changing primitive in
|
||||
* a collection (for example a total in a map) without needing to create new Integer/Long wrapper objects.
|
||||
*
|
||||
* @since 2.1
|
||||
* @param <T> the type to set and get
|
||||
* @version $Id: Mutable.java 1153213 2011-08-02 17:35:39Z ggregory $
|
||||
*/
|
||||
public interface Mutable<T> {
|
||||
|
||||
/**
|
||||
* Gets the value of this mutable.
|
||||
*
|
||||
* @return the stored value
|
||||
*/
|
||||
T getValue();
|
||||
|
||||
/**
|
||||
* Sets the value of this mutable.
|
||||
*
|
||||
* @param value
|
||||
* the value to store
|
||||
* @throws NullPointerException
|
||||
* if the object is null and null is invalid
|
||||
* @throws ClassCastException
|
||||
* if the type is invalid
|
||||
*/
|
||||
void setValue(T value);
|
||||
|
||||
}
|
||||
|
|
@ -1,273 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.mutable;
|
||||
|
||||
/**
|
||||
* A mutable <code>int</code> wrapper.
|
||||
* <p>
|
||||
* Note that as MutableInt does not extend Integer, it is not treated by String.format as an Integer parameter.
|
||||
*
|
||||
* @see Integer
|
||||
* @since 2.1
|
||||
* @version $Id: MutableInt.java 1160571 2011-08-23 07:36:08Z bayard $
|
||||
*/
|
||||
public class MutableInt extends Number implements Comparable<MutableInt>, Mutable<Number> {
|
||||
|
||||
/**
|
||||
* Required for serialization support.
|
||||
*
|
||||
* @see java.io.Serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 512176391864L;
|
||||
|
||||
/** The mutable value. */
|
||||
private int value;
|
||||
|
||||
/**
|
||||
* Constructs a new MutableInt with the default value of zero.
|
||||
*/
|
||||
public MutableInt() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new MutableInt with the specified value.
|
||||
*
|
||||
* @param value the initial value to store
|
||||
*/
|
||||
public MutableInt(int value) {
|
||||
super();
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new MutableInt with the specified value.
|
||||
*
|
||||
* @param value the initial value to store, not null
|
||||
* @throws NullPointerException if the object is null
|
||||
*/
|
||||
public MutableInt(Number value) {
|
||||
super();
|
||||
this.value = value.intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new MutableInt parsing the given string.
|
||||
*
|
||||
* @param value the string to parse, not null
|
||||
* @throws NumberFormatException if the string cannot be parsed into an int
|
||||
* @since 2.5
|
||||
*/
|
||||
public MutableInt(String value) throws NumberFormatException {
|
||||
super();
|
||||
this.value = Integer.parseInt(value);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Gets the value as a Integer instance.
|
||||
*
|
||||
* @return the value as a Integer, never null
|
||||
*/
|
||||
public Integer getValue() {
|
||||
return Integer.valueOf(this.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value.
|
||||
*
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void setValue(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value from any Number instance.
|
||||
*
|
||||
* @param value the value to set, not null
|
||||
* @throws NullPointerException if the object is null
|
||||
*/
|
||||
public void setValue(Number value) {
|
||||
this.value = value.intValue();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Increments the value.
|
||||
*
|
||||
* @since Commons Lang 2.2
|
||||
*/
|
||||
public void increment() {
|
||||
value++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the value.
|
||||
*
|
||||
* @since Commons Lang 2.2
|
||||
*/
|
||||
public void decrement() {
|
||||
value--;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Adds a value to the value of this instance.
|
||||
*
|
||||
* @param operand the value to add, not null
|
||||
* @since Commons Lang 2.2
|
||||
*/
|
||||
public void add(int operand) {
|
||||
this.value += operand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value to the value of this instance.
|
||||
*
|
||||
* @param operand the value to add, not null
|
||||
* @throws NullPointerException if the object is null
|
||||
* @since Commons Lang 2.2
|
||||
*/
|
||||
public void add(Number operand) {
|
||||
this.value += operand.intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts a value from the value of this instance.
|
||||
*
|
||||
* @param operand the value to subtract, not null
|
||||
* @since Commons Lang 2.2
|
||||
*/
|
||||
public void subtract(int operand) {
|
||||
this.value -= operand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts a value from the value of this instance.
|
||||
*
|
||||
* @param operand the value to subtract, not null
|
||||
* @throws NullPointerException if the object is null
|
||||
* @since Commons Lang 2.2
|
||||
*/
|
||||
public void subtract(Number operand) {
|
||||
this.value -= operand.intValue();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// shortValue and byteValue rely on Number implementation
|
||||
/**
|
||||
* Returns the value of this MutableInt as an int.
|
||||
*
|
||||
* @return the numeric value represented by this object after conversion to type int.
|
||||
*/
|
||||
@Override
|
||||
public int intValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this MutableInt as a long.
|
||||
*
|
||||
* @return the numeric value represented by this object after conversion to type long.
|
||||
*/
|
||||
@Override
|
||||
public long longValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this MutableInt as a float.
|
||||
*
|
||||
* @return the numeric value represented by this object after conversion to type float.
|
||||
*/
|
||||
@Override
|
||||
public float floatValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this MutableInt as a double.
|
||||
*
|
||||
* @return the numeric value represented by this object after conversion to type double.
|
||||
*/
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Gets this mutable as an instance of Integer.
|
||||
*
|
||||
* @return a Integer instance containing the value from this mutable, never null
|
||||
*/
|
||||
public Integer toInteger() {
|
||||
return Integer.valueOf(intValue());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Compares this object to the specified object. The result is <code>true</code> if and only if the argument is
|
||||
* not <code>null</code> and is a <code>MutableInt</code> object that contains the same <code>int</code> value
|
||||
* as this object.
|
||||
*
|
||||
* @param obj the object to compare with, null returns false
|
||||
* @return <code>true</code> if the objects are the same; <code>false</code> otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof MutableInt) {
|
||||
return value == ((MutableInt) obj).intValue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a suitable hash code for this mutable.
|
||||
*
|
||||
* @return a suitable hash code
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Compares this mutable to another in ascending order.
|
||||
*
|
||||
* @param other the other mutable to compare to, not null
|
||||
* @return negative if this is less, zero if equal, positive if greater
|
||||
*/
|
||||
public int compareTo(MutableInt other) {
|
||||
int anotherVal = other.value;
|
||||
return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Returns the String value of this mutable.
|
||||
*
|
||||
* @return the mutable value as a string
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you 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.
|
||||
-->
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
Provides typed mutable wrappers to primitive values and Object.
|
||||
@since 2.1
|
||||
<p>These classes are not thread-safe.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
This document is the API specification for the Apache Commons Lang library.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
Provides highly reusable static utility methods, chiefly concerned
|
||||
with adding value to the {@link java.lang} classes.
|
||||
@since 1.0
|
||||
<p>Most of these classes are immutable and thus thread-safe.
|
||||
However Charset is not currently guaranteed thread-safe under all circumstances.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.reflect;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import external.org.apache.commons.lang3.ClassUtils;
|
||||
|
||||
/**
|
||||
* Contains common code for working with Methods/Constructors, extracted and
|
||||
* refactored from <code>MethodUtils</code> when it was imported from Commons
|
||||
* BeanUtils.
|
||||
*
|
||||
* @since 2.5
|
||||
* @version $Id: MemberUtils.java 1143537 2011-07-06 19:30:22Z joehni $
|
||||
*/
|
||||
public abstract class MemberUtils {
|
||||
// TODO extract an interface to implement compareParameterSets(...)?
|
||||
|
||||
private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
|
||||
|
||||
/** Array of primitive number types ordered by "promotability" */
|
||||
private static final Class<?>[] ORDERED_PRIMITIVE_TYPES = { Byte.TYPE, Short.TYPE,
|
||||
Character.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE };
|
||||
|
||||
/**
|
||||
* XXX Default access superclass workaround
|
||||
*
|
||||
* When a public class has a default access superclass with public members,
|
||||
* these members are accessible. Calling them from compiled code works fine.
|
||||
* Unfortunately, on some JVMs, using reflection to invoke these members
|
||||
* seems to (wrongly) prevent access even when the modifier is public.
|
||||
* Calling setAccessible(true) solves the problem but will only work from
|
||||
* sufficiently privileged code. Better workarounds would be gratefully
|
||||
* accepted.
|
||||
* @param o the AccessibleObject to set as accessible
|
||||
*/
|
||||
static void setAccessibleWorkaround(AccessibleObject o) {
|
||||
if (o == null || o.isAccessible()) {
|
||||
return;
|
||||
}
|
||||
Member m = (Member) o;
|
||||
if (Modifier.isPublic(m.getModifiers())
|
||||
&& isPackageAccess(m.getDeclaringClass().getModifiers())) {
|
||||
try {
|
||||
o.setAccessible(true);
|
||||
} catch (SecurityException e) { // NOPMD
|
||||
// ignore in favor of subsequent IllegalAccessException
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a given set of modifiers implies package access.
|
||||
* @param modifiers to test
|
||||
* @return true unless package/protected/private modifier detected
|
||||
*/
|
||||
static boolean isPackageAccess(int modifiers) {
|
||||
return (modifiers & ACCESS_TEST) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a Member is accessible.
|
||||
* @param m Member to check
|
||||
* @return true if <code>m</code> is accessible
|
||||
*/
|
||||
static boolean isAccessible(Member m) {
|
||||
return m != null && Modifier.isPublic(m.getModifiers()) && !m.isSynthetic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the relative fitness of two sets of parameter types in terms of
|
||||
* matching a third set of runtime parameter types, such that a list ordered
|
||||
* by the results of the comparison would return the best match first
|
||||
* (least).
|
||||
*
|
||||
* @param left the "left" parameter set
|
||||
* @param right the "right" parameter set
|
||||
* @param actual the runtime parameter types to match against
|
||||
* <code>left</code>/<code>right</code>
|
||||
* @return int consistent with <code>compare</code> semantics
|
||||
*/
|
||||
public static int compareParameterTypes(Class<?>[] left, Class<?>[] right, Class<?>[] actual) {
|
||||
float leftCost = getTotalTransformationCost(actual, left);
|
||||
float rightCost = getTotalTransformationCost(actual, right);
|
||||
return leftCost < rightCost ? -1 : rightCost < leftCost ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of the object transformation cost for each class in the
|
||||
* source argument list.
|
||||
* @param srcArgs The source arguments
|
||||
* @param destArgs The destination arguments
|
||||
* @return The total transformation cost
|
||||
*/
|
||||
private static float getTotalTransformationCost(Class<?>[] srcArgs, Class<?>[] destArgs) {
|
||||
float totalCost = 0.0f;
|
||||
for (int i = 0; i < srcArgs.length; i++) {
|
||||
Class<?> srcClass, destClass;
|
||||
srcClass = srcArgs[i];
|
||||
destClass = destArgs[i];
|
||||
totalCost += getObjectTransformationCost(srcClass, destClass);
|
||||
}
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of steps required needed to turn the source class into
|
||||
* the destination class. This represents the number of steps in the object
|
||||
* hierarchy graph.
|
||||
* @param srcClass The source class
|
||||
* @param destClass The destination class
|
||||
* @return The cost of transforming an object
|
||||
*/
|
||||
private static float getObjectTransformationCost(Class<?> srcClass, Class<?> destClass) {
|
||||
if (destClass.isPrimitive()) {
|
||||
return getPrimitivePromotionCost(srcClass, destClass);
|
||||
}
|
||||
float cost = 0.0f;
|
||||
while (srcClass != null && !destClass.equals(srcClass)) {
|
||||
if (destClass.isInterface() && ClassUtils.isAssignable(srcClass, destClass)) {
|
||||
// slight penalty for interface match.
|
||||
// we still want an exact match to override an interface match,
|
||||
// but
|
||||
// an interface match should override anything where we have to
|
||||
// get a superclass.
|
||||
cost += 0.25f;
|
||||
break;
|
||||
}
|
||||
cost++;
|
||||
srcClass = srcClass.getSuperclass();
|
||||
}
|
||||
/*
|
||||
* If the destination class is null, we've travelled all the way up to
|
||||
* an Object match. We'll penalize this by adding 1.5 to the cost.
|
||||
*/
|
||||
if (srcClass == null) {
|
||||
cost += 1.5f;
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of steps required to promote a primitive number to another
|
||||
* type.
|
||||
* @param srcClass the (primitive) source class
|
||||
* @param destClass the (primitive) destination class
|
||||
* @return The cost of promoting the primitive
|
||||
*/
|
||||
private static float getPrimitivePromotionCost(final Class<?> srcClass, final Class<?> destClass) {
|
||||
float cost = 0.0f;
|
||||
Class<?> cls = srcClass;
|
||||
if (!cls.isPrimitive()) {
|
||||
// slight unwrapping penalty
|
||||
cost += 0.1f;
|
||||
cls = ClassUtils.wrapperToPrimitive(cls);
|
||||
}
|
||||
for (int i = 0; cls != destClass && i < ORDERED_PRIMITIVE_TYPES.length; i++) {
|
||||
if (cls == ORDERED_PRIMITIVE_TYPES[i]) {
|
||||
cost += 0.1f;
|
||||
if (i < ORDERED_PRIMITIVE_TYPES.length - 1) {
|
||||
cls = ORDERED_PRIMITIVE_TYPES[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,537 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.reflect;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import external.org.apache.commons.lang3.ArrayUtils;
|
||||
import external.org.apache.commons.lang3.ClassUtils;
|
||||
|
||||
/**
|
||||
* <p>Utility reflection methods focused on methods, originally from Commons BeanUtils.
|
||||
* Differences from the BeanUtils version may be noted, especially where similar functionality
|
||||
* already existed within Lang.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Known Limitations</h3>
|
||||
* <h4>Accessing Public Methods In A Default Access Superclass</h4>
|
||||
* <p>There is an issue when invoking public methods contained in a default access superclass on JREs prior to 1.4.
|
||||
* Reflection locates these methods fine and correctly assigns them as public.
|
||||
* However, an <code>IllegalAccessException</code> is thrown if the method is invoked.</p>
|
||||
*
|
||||
* <p><code>MethodUtils</code> contains a workaround for this situation.
|
||||
* It will attempt to call <code>setAccessible</code> on this method.
|
||||
* If this call succeeds, then the method can be invoked as normal.
|
||||
* This call will only succeed when the application has sufficient security privileges.
|
||||
* If this call fails then the method may fail.</p>
|
||||
*
|
||||
* @since 2.5
|
||||
* @version $Id: MethodUtils.java 1166253 2011-09-07 16:27:42Z ggregory $
|
||||
*/
|
||||
public class MethodUtils {
|
||||
|
||||
/**
|
||||
* <p>MethodUtils instances should NOT be constructed in standard programming.
|
||||
* Instead, the class should be used as
|
||||
* <code>MethodUtils.getAccessibleMethod(method)</code>.</p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean
|
||||
* instance to operate.</p>
|
||||
*/
|
||||
public MethodUtils() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Invokes a named method whose parameter type matches the object type.</p>
|
||||
*
|
||||
* <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
|
||||
*
|
||||
* <p>This method supports calls to methods taking primitive parameters
|
||||
* via passing in wrapping classes. So, for example, a <code>Boolean</code> object
|
||||
* would match a <code>boolean</code> primitive.</p>
|
||||
*
|
||||
* <p>This is a convenient wrapper for
|
||||
* {@link #invokeMethod(Object object,String methodName, Object[] args, Class[] parameterTypes)}.
|
||||
* </p>
|
||||
*
|
||||
* @param object invoke method on this object
|
||||
* @param methodName get method with this name
|
||||
* @param args use these arguments - treat null as empty array
|
||||
* @return The value returned by the invoked method
|
||||
*
|
||||
* @throws NoSuchMethodException if there is no such accessible method
|
||||
* @throws InvocationTargetException wraps an exception thrown by the method invoked
|
||||
* @throws IllegalAccessException if the requested method is not accessible via reflection
|
||||
*/
|
||||
public static Object invokeMethod(Object object, String methodName,
|
||||
Object... args) throws NoSuchMethodException,
|
||||
IllegalAccessException, InvocationTargetException {
|
||||
if (args == null) {
|
||||
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
int arguments = args.length;
|
||||
Class<?>[] parameterTypes = new Class[arguments];
|
||||
for (int i = 0; i < arguments; i++) {
|
||||
parameterTypes[i] = args[i].getClass();
|
||||
}
|
||||
return invokeMethod(object, methodName, args, parameterTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Invokes a named method whose parameter type matches the object type.</p>
|
||||
*
|
||||
* <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
|
||||
*
|
||||
* <p>This method supports calls to methods taking primitive parameters
|
||||
* via passing in wrapping classes. So, for example, a <code>Boolean</code> object
|
||||
* would match a <code>boolean</code> primitive.</p>
|
||||
*
|
||||
* @param object invoke method on this object
|
||||
* @param methodName get method with this name
|
||||
* @param args use these arguments - treat null as empty array
|
||||
* @param parameterTypes match these parameters - treat null as empty array
|
||||
* @return The value returned by the invoked method
|
||||
*
|
||||
* @throws NoSuchMethodException if there is no such accessible method
|
||||
* @throws InvocationTargetException wraps an exception thrown by the method invoked
|
||||
* @throws IllegalAccessException if the requested method is not accessible via reflection
|
||||
*/
|
||||
public static Object invokeMethod(Object object, String methodName,
|
||||
Object[] args, Class<?>[] parameterTypes)
|
||||
throws NoSuchMethodException, IllegalAccessException,
|
||||
InvocationTargetException {
|
||||
if (parameterTypes == null) {
|
||||
parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
|
||||
}
|
||||
if (args == null) {
|
||||
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
Method method = getMatchingAccessibleMethod(object.getClass(),
|
||||
methodName, parameterTypes);
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException("No such accessible method: "
|
||||
+ methodName + "() on object: "
|
||||
+ object.getClass().getName());
|
||||
}
|
||||
return method.invoke(object, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Invokes a method whose parameter types match exactly the object
|
||||
* types.</p>
|
||||
*
|
||||
* <p>This uses reflection to invoke the method obtained from a call to
|
||||
* <code>getAccessibleMethod()</code>.</p>
|
||||
*
|
||||
* @param object invoke method on this object
|
||||
* @param methodName get method with this name
|
||||
* @param args use these arguments - treat null as empty array
|
||||
* @return The value returned by the invoked method
|
||||
*
|
||||
* @throws NoSuchMethodException if there is no such accessible method
|
||||
* @throws InvocationTargetException wraps an exception thrown by the
|
||||
* method invoked
|
||||
* @throws IllegalAccessException if the requested method is not accessible
|
||||
* via reflection
|
||||
*/
|
||||
public static Object invokeExactMethod(Object object, String methodName,
|
||||
Object... args) throws NoSuchMethodException,
|
||||
IllegalAccessException, InvocationTargetException {
|
||||
if (args == null) {
|
||||
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
int arguments = args.length;
|
||||
Class<?>[] parameterTypes = new Class[arguments];
|
||||
for (int i = 0; i < arguments; i++) {
|
||||
parameterTypes[i] = args[i].getClass();
|
||||
}
|
||||
return invokeExactMethod(object, methodName, args, parameterTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Invokes a method whose parameter types match exactly the parameter
|
||||
* types given.</p>
|
||||
*
|
||||
* <p>This uses reflection to invoke the method obtained from a call to
|
||||
* <code>getAccessibleMethod()</code>.</p>
|
||||
*
|
||||
* @param object invoke method on this object
|
||||
* @param methodName get method with this name
|
||||
* @param args use these arguments - treat null as empty array
|
||||
* @param parameterTypes match these parameters - treat null as empty array
|
||||
* @return The value returned by the invoked method
|
||||
*
|
||||
* @throws NoSuchMethodException if there is no such accessible method
|
||||
* @throws InvocationTargetException wraps an exception thrown by the
|
||||
* method invoked
|
||||
* @throws IllegalAccessException if the requested method is not accessible
|
||||
* via reflection
|
||||
*/
|
||||
public static Object invokeExactMethod(Object object, String methodName,
|
||||
Object[] args, Class<?>[] parameterTypes)
|
||||
throws NoSuchMethodException, IllegalAccessException,
|
||||
InvocationTargetException {
|
||||
if (args == null) {
|
||||
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
if (parameterTypes == null) {
|
||||
parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
|
||||
}
|
||||
Method method = getAccessibleMethod(object.getClass(), methodName,
|
||||
parameterTypes);
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException("No such accessible method: "
|
||||
+ methodName + "() on object: "
|
||||
+ object.getClass().getName());
|
||||
}
|
||||
return method.invoke(object, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Invokes a static method whose parameter types match exactly the parameter
|
||||
* types given.</p>
|
||||
*
|
||||
* <p>This uses reflection to invoke the method obtained from a call to
|
||||
* {@link #getAccessibleMethod(Class, String, Class[])}.</p>
|
||||
*
|
||||
* @param cls invoke static method on this class
|
||||
* @param methodName get method with this name
|
||||
* @param args use these arguments - treat null as empty array
|
||||
* @param parameterTypes match these parameters - treat null as empty array
|
||||
* @return The value returned by the invoked method
|
||||
*
|
||||
* @throws NoSuchMethodException if there is no such accessible method
|
||||
* @throws InvocationTargetException wraps an exception thrown by the
|
||||
* method invoked
|
||||
* @throws IllegalAccessException if the requested method is not accessible
|
||||
* via reflection
|
||||
*/
|
||||
public static Object invokeExactStaticMethod(Class<?> cls, String methodName,
|
||||
Object[] args, Class<?>[] parameterTypes)
|
||||
throws NoSuchMethodException, IllegalAccessException,
|
||||
InvocationTargetException {
|
||||
if (args == null) {
|
||||
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
if (parameterTypes == null) {
|
||||
parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
|
||||
}
|
||||
Method method = getAccessibleMethod(cls, methodName, parameterTypes);
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException("No such accessible method: "
|
||||
+ methodName + "() on class: " + cls.getName());
|
||||
}
|
||||
return method.invoke(null, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Invokes a named static method whose parameter type matches the object type.</p>
|
||||
*
|
||||
* <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
|
||||
*
|
||||
* <p>This method supports calls to methods taking primitive parameters
|
||||
* via passing in wrapping classes. So, for example, a <code>Boolean</code> class
|
||||
* would match a <code>boolean</code> primitive.</p>
|
||||
*
|
||||
* <p>This is a convenient wrapper for
|
||||
* {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}.
|
||||
* </p>
|
||||
*
|
||||
* @param cls invoke static method on this class
|
||||
* @param methodName get method with this name
|
||||
* @param args use these arguments - treat null as empty array
|
||||
* @return The value returned by the invoked method
|
||||
*
|
||||
* @throws NoSuchMethodException if there is no such accessible method
|
||||
* @throws InvocationTargetException wraps an exception thrown by the
|
||||
* method invoked
|
||||
* @throws IllegalAccessException if the requested method is not accessible
|
||||
* via reflection
|
||||
*/
|
||||
public static Object invokeStaticMethod(Class<?> cls, String methodName,
|
||||
Object... args) throws NoSuchMethodException,
|
||||
IllegalAccessException, InvocationTargetException {
|
||||
if (args == null) {
|
||||
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
int arguments = args.length;
|
||||
Class<?>[] parameterTypes = new Class[arguments];
|
||||
for (int i = 0; i < arguments; i++) {
|
||||
parameterTypes[i] = args[i].getClass();
|
||||
}
|
||||
return invokeStaticMethod(cls, methodName, args, parameterTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Invokes a named static method whose parameter type matches the object type.</p>
|
||||
*
|
||||
* <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
|
||||
*
|
||||
* <p>This method supports calls to methods taking primitive parameters
|
||||
* via passing in wrapping classes. So, for example, a <code>Boolean</code> class
|
||||
* would match a <code>boolean</code> primitive.</p>
|
||||
*
|
||||
*
|
||||
* @param cls invoke static method on this class
|
||||
* @param methodName get method with this name
|
||||
* @param args use these arguments - treat null as empty array
|
||||
* @param parameterTypes match these parameters - treat null as empty array
|
||||
* @return The value returned by the invoked method
|
||||
*
|
||||
* @throws NoSuchMethodException if there is no such accessible method
|
||||
* @throws InvocationTargetException wraps an exception thrown by the
|
||||
* method invoked
|
||||
* @throws IllegalAccessException if the requested method is not accessible
|
||||
* via reflection
|
||||
*/
|
||||
public static Object invokeStaticMethod(Class<?> cls, String methodName,
|
||||
Object[] args, Class<?>[] parameterTypes)
|
||||
throws NoSuchMethodException, IllegalAccessException,
|
||||
InvocationTargetException {
|
||||
if (parameterTypes == null) {
|
||||
parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
|
||||
}
|
||||
if (args == null) {
|
||||
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
Method method = getMatchingAccessibleMethod(cls, methodName,
|
||||
parameterTypes);
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException("No such accessible method: "
|
||||
+ methodName + "() on class: " + cls.getName());
|
||||
}
|
||||
return method.invoke(null, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Invokes a static method whose parameter types match exactly the object
|
||||
* types.</p>
|
||||
*
|
||||
* <p>This uses reflection to invoke the method obtained from a call to
|
||||
* {@link #getAccessibleMethod(Class, String, Class[])}.</p>
|
||||
*
|
||||
* @param cls invoke static method on this class
|
||||
* @param methodName get method with this name
|
||||
* @param args use these arguments - treat null as empty array
|
||||
* @return The value returned by the invoked method
|
||||
*
|
||||
* @throws NoSuchMethodException if there is no such accessible method
|
||||
* @throws InvocationTargetException wraps an exception thrown by the
|
||||
* method invoked
|
||||
* @throws IllegalAccessException if the requested method is not accessible
|
||||
* via reflection
|
||||
*/
|
||||
public static Object invokeExactStaticMethod(Class<?> cls, String methodName,
|
||||
Object... args) throws NoSuchMethodException,
|
||||
IllegalAccessException, InvocationTargetException {
|
||||
if (args == null) {
|
||||
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
int arguments = args.length;
|
||||
Class<?>[] parameterTypes = new Class[arguments];
|
||||
for (int i = 0; i < arguments; i++) {
|
||||
parameterTypes[i] = args[i].getClass();
|
||||
}
|
||||
return invokeExactStaticMethod(cls, methodName, args, parameterTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns an accessible method (that is, one that can be invoked via
|
||||
* reflection) with given name and parameters. If no such method
|
||||
* can be found, return <code>null</code>.
|
||||
* This is just a convenient wrapper for
|
||||
* {@link #getAccessibleMethod(Method method)}.</p>
|
||||
*
|
||||
* @param cls get method from this class
|
||||
* @param methodName get method with this name
|
||||
* @param parameterTypes with these parameters types
|
||||
* @return The accessible method
|
||||
*/
|
||||
public static Method getAccessibleMethod(Class<?> cls, String methodName,
|
||||
Class<?>... parameterTypes) {
|
||||
try {
|
||||
return getAccessibleMethod(cls.getMethod(methodName,
|
||||
parameterTypes));
|
||||
} catch (NoSuchMethodException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns an accessible method (that is, one that can be invoked via
|
||||
* reflection) that implements the specified Method. If no such method
|
||||
* can be found, return <code>null</code>.</p>
|
||||
*
|
||||
* @param method The method that we wish to call
|
||||
* @return The accessible method
|
||||
*/
|
||||
public static Method getAccessibleMethod(Method method) {
|
||||
if (!MemberUtils.isAccessible(method)) {
|
||||
return null;
|
||||
}
|
||||
// If the declaring class is public, we are done
|
||||
Class<?> cls = method.getDeclaringClass();
|
||||
if (Modifier.isPublic(cls.getModifiers())) {
|
||||
return method;
|
||||
}
|
||||
String methodName = method.getName();
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
|
||||
// Check the implemented interfaces and subinterfaces
|
||||
method = getAccessibleMethodFromInterfaceNest(cls, methodName,
|
||||
parameterTypes);
|
||||
|
||||
// Check the superclass chain
|
||||
if (method == null) {
|
||||
method = getAccessibleMethodFromSuperclass(cls, methodName,
|
||||
parameterTypes);
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns an accessible method (that is, one that can be invoked via
|
||||
* reflection) by scanning through the superclasses. If no such method
|
||||
* can be found, return <code>null</code>.</p>
|
||||
*
|
||||
* @param cls Class to be checked
|
||||
* @param methodName Method name of the method we wish to call
|
||||
* @param parameterTypes The parameter type signatures
|
||||
* @return the accessible method or <code>null</code> if not found
|
||||
*/
|
||||
private static Method getAccessibleMethodFromSuperclass(Class<?> cls,
|
||||
String methodName, Class<?>... parameterTypes) {
|
||||
Class<?> parentClass = cls.getSuperclass();
|
||||
while (parentClass != null) {
|
||||
if (Modifier.isPublic(parentClass.getModifiers())) {
|
||||
try {
|
||||
return parentClass.getMethod(methodName, parameterTypes);
|
||||
} catch (NoSuchMethodException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
parentClass = parentClass.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns an accessible method (that is, one that can be invoked via
|
||||
* reflection) that implements the specified method, by scanning through
|
||||
* all implemented interfaces and subinterfaces. If no such method
|
||||
* can be found, return <code>null</code>.</p>
|
||||
*
|
||||
* <p>There isn't any good reason why this method must be private.
|
||||
* It is because there doesn't seem any reason why other classes should
|
||||
* call this rather than the higher level methods.</p>
|
||||
*
|
||||
* @param cls Parent class for the interfaces to be checked
|
||||
* @param methodName Method name of the method we wish to call
|
||||
* @param parameterTypes The parameter type signatures
|
||||
* @return the accessible method or <code>null</code> if not found
|
||||
*/
|
||||
private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls,
|
||||
String methodName, Class<?>... parameterTypes) {
|
||||
Method method = null;
|
||||
|
||||
// Search up the superclass chain
|
||||
for (; cls != null; cls = cls.getSuperclass()) {
|
||||
|
||||
// Check the implemented interfaces of the parent class
|
||||
Class<?>[] interfaces = cls.getInterfaces();
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
// Is this interface public?
|
||||
if (!Modifier.isPublic(interfaces[i].getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
// Does the method exist on this interface?
|
||||
try {
|
||||
method = interfaces[i].getDeclaredMethod(methodName,
|
||||
parameterTypes);
|
||||
} catch (NoSuchMethodException e) { // NOPMD
|
||||
/*
|
||||
* Swallow, if no method is found after the loop then this
|
||||
* method returns null.
|
||||
*/
|
||||
}
|
||||
if (method != null) {
|
||||
break;
|
||||
}
|
||||
// Recursively check our parent interfaces
|
||||
method = getAccessibleMethodFromInterfaceNest(interfaces[i],
|
||||
methodName, parameterTypes);
|
||||
if (method != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Finds an accessible method that matches the given name and has compatible parameters.
|
||||
* Compatible parameters mean that every method parameter is assignable from
|
||||
* the given parameters.
|
||||
* In other words, it finds a method with the given name
|
||||
* that will take the parameters given.<p>
|
||||
*
|
||||
* <p>This method is used by
|
||||
* {@link
|
||||
* #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
|
||||
*
|
||||
* <p>This method can match primitive parameter by passing in wrapper classes.
|
||||
* For example, a <code>Boolean</code> will match a primitive <code>boolean</code>
|
||||
* parameter.
|
||||
*
|
||||
* @param cls find method in this class
|
||||
* @param methodName find method with this name
|
||||
* @param parameterTypes find method with most compatible parameters
|
||||
* @return The accessible method
|
||||
*/
|
||||
public static Method getMatchingAccessibleMethod(Class<?> cls,
|
||||
String methodName, Class<?>... parameterTypes) {
|
||||
try {
|
||||
Method method = cls.getMethod(methodName, parameterTypes);
|
||||
MemberUtils.setAccessibleWorkaround(method);
|
||||
return method;
|
||||
} catch (NoSuchMethodException e) { // NOPMD - Swallow the exception
|
||||
}
|
||||
// search through all methods
|
||||
Method bestMatch = null;
|
||||
Method[] methods = cls.getMethods();
|
||||
for (Method method : methods) {
|
||||
// compare name and parameters
|
||||
if (method.getName().equals(methodName) && ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true)) {
|
||||
// get accessible version of method
|
||||
Method accessibleMethod = getAccessibleMethod(method);
|
||||
if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareParameterTypes(
|
||||
accessibleMethod.getParameterTypes(),
|
||||
bestMatch.getParameterTypes(),
|
||||
parameterTypes) < 0)) {
|
||||
bestMatch = accessibleMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestMatch != null) {
|
||||
MemberUtils.setAccessibleWorkaround(bestMatch);
|
||||
}
|
||||
return bestMatch;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you 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.
|
||||
-->
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
Accumulates common high-level uses of the <code>java.lang.reflect</code> APIs.
|
||||
@since 3.0
|
||||
<p>These classes are immutable, and therefore thread-safe.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.tuple;
|
||||
|
||||
/**
|
||||
* <p>An immutable pair consisting of two {@code Object} elements.</p>
|
||||
*
|
||||
* <p>Although the implementation is immutable, there is no restriction on the objects
|
||||
* that may be stored. If mutable objects are stored in the pair, then the pair
|
||||
* itself effectively becomes mutable. The class is also not {@code final}, so a subclass
|
||||
* could add undesirable behaviour.</p>
|
||||
*
|
||||
* <p>#ThreadSafe# if the objects are threadsafe</p>
|
||||
*
|
||||
* @param <L> the left element type
|
||||
* @param <R> the right element type
|
||||
*
|
||||
* @since Lang 3.0
|
||||
* @version $Id: ImmutablePair.java 1127544 2011-05-25 14:35:42Z scolebourne $
|
||||
*/
|
||||
public final class ImmutablePair<L, R> extends Pair<L, R> {
|
||||
|
||||
/** Serialization version */
|
||||
private static final long serialVersionUID = 4954918890077093841L;
|
||||
|
||||
/** Left object */
|
||||
public final L left;
|
||||
/** Right object */
|
||||
public final R right;
|
||||
|
||||
/**
|
||||
* <p>Obtains an immutable pair of from two objects inferring the generic types.</p>
|
||||
*
|
||||
* <p>This factory allows the pair to be created using inference to
|
||||
* obtain the generic types.</p>
|
||||
*
|
||||
* @param <L> the left element type
|
||||
* @param <R> the right element type
|
||||
* @param left the left element, may be null
|
||||
* @param right the right element, may be null
|
||||
* @return a pair formed from the two parameters, not null
|
||||
*/
|
||||
public static <L, R> ImmutablePair<L, R> of(L left, R right) {
|
||||
return new ImmutablePair<L, R>(left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new pair instance.
|
||||
*
|
||||
* @param left the left value, may be null
|
||||
* @param right the right value, may be null
|
||||
*/
|
||||
public ImmutablePair(L left, R right) {
|
||||
super();
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public L getLeft() {
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public R getRight() {
|
||||
return right;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Throws {@code UnsupportedOperationException}.</p>
|
||||
*
|
||||
* <p>This pair is immutable, so this operation is not supported.</p>
|
||||
*
|
||||
* @param value the value to set
|
||||
* @return never
|
||||
* @throws UnsupportedOperationException as this operation is not supported
|
||||
*/
|
||||
public R setValue(R value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 external.org.apache.commons.lang3.tuple;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
import external.org.apache.commons.lang3.ObjectUtils;
|
||||
import external.org.apache.commons.lang3.builder.CompareToBuilder;
|
||||
|
||||
/**
|
||||
* <p>A pair consisting of two elements.</p>
|
||||
*
|
||||
* <p>This class is an abstract implementation defining the basic API.
|
||||
* It refers to the elements as 'left' and 'right'. It also implements the
|
||||
* {@code Map.Entry} interface where the key is 'left' and the value is 'right'.</p>
|
||||
*
|
||||
* <p>Subclass implementations may be mutable or immutable.
|
||||
* However, there is no restriction on the type of the stored objects that may be stored.
|
||||
* If mutable objects are stored in the pair, then the pair itself effectively becomes mutable.</p>
|
||||
*
|
||||
* @param <L> the left element type
|
||||
* @param <R> the right element type
|
||||
*
|
||||
* @since Lang 3.0
|
||||
* @version $Id: Pair.java 1142401 2011-07-03 08:30:12Z bayard $
|
||||
*/
|
||||
public abstract class Pair<L, R> implements Map.Entry<L, R>, Comparable<Pair<L, R>>, Serializable {
|
||||
|
||||
/** Serialization version */
|
||||
private static final long serialVersionUID = 4954918890077093841L;
|
||||
|
||||
/**
|
||||
* <p>Obtains an immutable pair of from two objects inferring the generic types.</p>
|
||||
*
|
||||
* <p>This factory allows the pair to be created using inference to
|
||||
* obtain the generic types.</p>
|
||||
*
|
||||
* @param <L> the left element type
|
||||
* @param <R> the right element type
|
||||
* @param left the left element, may be null
|
||||
* @param right the right element, may be null
|
||||
* @return a pair formed from the two parameters, not null
|
||||
*/
|
||||
public static <L, R> Pair<L, R> of(L left, R right) {
|
||||
return new ImmutablePair<L, R>(left, right);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Gets the left element from this pair.</p>
|
||||
*
|
||||
* <p>When treated as a key-value pair, this is the key.</p>
|
||||
*
|
||||
* @return the left element, may be null
|
||||
*/
|
||||
public abstract L getLeft();
|
||||
|
||||
/**
|
||||
* <p>Gets the right element from this pair.</p>
|
||||
*
|
||||
* <p>When treated as a key-value pair, this is the value.</p>
|
||||
*
|
||||
* @return the right element, may be null
|
||||
*/
|
||||
public abstract R getRight();
|
||||
|
||||
/**
|
||||
* <p>Gets the key from this pair.</p>
|
||||
*
|
||||
* <p>This method implements the {@code Map.Entry} interface returning the
|
||||
* left element as the key.</p>
|
||||
*
|
||||
* @return the left element as the key, may be null
|
||||
*/
|
||||
public final L getKey() {
|
||||
return getLeft();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the value from this pair.</p>
|
||||
*
|
||||
* <p>This method implements the {@code Map.Entry} interface returning the
|
||||
* right element as the value.</p>
|
||||
*
|
||||
* @return the right element as the value, may be null
|
||||
*/
|
||||
public R getValue() {
|
||||
return getRight();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Compares the pair based on the left element followed by the right element.
|
||||
* The types must be {@code Comparable}.</p>
|
||||
*
|
||||
* @param other the other pair, not null
|
||||
* @return negative if this is less, zero if equal, positive if greater
|
||||
*/
|
||||
public int compareTo(Pair<L, R> other) {
|
||||
return new CompareToBuilder().append(getLeft(), other.getLeft())
|
||||
.append(getRight(), other.getRight()).toComparison();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Compares this pair to another based on the two elements.</p>
|
||||
*
|
||||
* @param obj the object to compare to, null returns false
|
||||
* @return true if the elements of the pair are equal
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof Map.Entry<?, ?>) {
|
||||
Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj;
|
||||
return ObjectUtils.equals(getKey(), other.getKey())
|
||||
&& ObjectUtils.equals(getValue(), other.getValue());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns a suitable hash code.
|
||||
* The hash code follows the definition in {@code Map.Entry}.</p>
|
||||
*
|
||||
* @return the hash code
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// see Map.Entry API specification
|
||||
return (getKey() == null ? 0 : getKey().hashCode()) ^
|
||||
(getValue() == null ? 0 : getValue().hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns a String representation of this pair using the format {@code ($left,$right)}.</p>
|
||||
*
|
||||
* @return a string describing this object, not null
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder().append('(').append(getLeft()).append(',').append(getRight()).append(')').toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Formats the receiver using the given format.</p>
|
||||
*
|
||||
* <p>This uses {@link java.util.Formattable} to perform the formatting. Two variables may
|
||||
* be used to embed the left and right elements. Use {@code %1$s} for the left
|
||||
* element (key) and {@code %2$s} for the right element (value).
|
||||
* The default format used by {@code toString()} is {@code (%1$s,%2$s)}.</p>
|
||||
*
|
||||
* @param format the format string, optionally containing {@code %1$s} and {@code %2$s}, not null
|
||||
* @return the formatted string, not null
|
||||
*/
|
||||
public String toString(String format) {
|
||||
return String.format(format, getLeft(), getRight());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
Tuple classes, starting with a Pair class in version 3.0.
|
||||
@since 3.0
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 org.apache.commons.lang3.reflect;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class MemberUtilsX {
|
||||
public static int compareConstructorFit(final Constructor<?> left, final Constructor<?> right, final Class<?>[] actual) {
|
||||
return MemberUtils.compareConstructorFit(left, right, actual);
|
||||
}
|
||||
|
||||
public static int compareMethodFit(final Method left, final Method right, final Class<?>[] actual) {
|
||||
return MemberUtils.compareMethodFit(left, right, actual);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android;
|
||||
|
||||
public interface ResConst {
|
||||
int RES_STRING_POOL_TYPE = 0x0001;
|
||||
int RES_TABLE_TYPE = 0x0002;
|
||||
int RES_TABLE_PACKAGE_TYPE = 0x0200;
|
||||
int RES_TABLE_TYPE_SPEC_TYPE = 0x0202;
|
||||
int RES_TABLE_TYPE_TYPE = 0x0201;
|
||||
|
||||
int RES_XML_TYPE = 0x0003;
|
||||
int RES_XML_RESOURCE_MAP_TYPE = 0x0180;
|
||||
int RES_XML_END_NAMESPACE_TYPE = 0x0101;
|
||||
int RES_XML_END_ELEMENT_TYPE = 0x0103;
|
||||
int RES_XML_START_NAMESPACE_TYPE = 0x0100;
|
||||
int RES_XML_START_ELEMENT_TYPE = 0x0102;
|
||||
int RES_XML_CDATA_TYPE = 0x0104;
|
||||
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android;
|
||||
|
||||
public class StringItem {
|
||||
public String data;
|
||||
public int dataOffset;
|
||||
public int index;
|
||||
|
||||
public StringItem() {
|
||||
super();
|
||||
}
|
||||
|
||||
public StringItem(String data) {
|
||||
super();
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
StringItem other = (StringItem) obj;
|
||||
if (data == null) {
|
||||
if (other.data != null)
|
||||
return false;
|
||||
} else if (!data.equals(other.data))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((data == null) ? 0 : data.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("S%04d %s", index, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class StringItems extends ArrayList<StringItem> {
|
||||
private static final int UTF8_FLAG = 0x00000100;
|
||||
|
||||
|
||||
public static String[] read(ByteBuffer in) throws IOException {
|
||||
int trunkOffset = in.position() - 8;
|
||||
int stringCount = in.getInt();
|
||||
int styleOffsetCount = in.getInt();
|
||||
int flags = in.getInt();
|
||||
int stringDataOffset = in.getInt();
|
||||
int stylesOffset = in.getInt();
|
||||
int offsets[] = new int[stringCount];
|
||||
String strings[] = new String[stringCount];
|
||||
for (int i = 0; i < stringCount; i++) {
|
||||
offsets[i] = in.getInt();
|
||||
}
|
||||
|
||||
int base = trunkOffset + stringDataOffset;
|
||||
for (int i = 0; i < offsets.length; i++) {
|
||||
in.position(base + offsets[i]);
|
||||
String s;
|
||||
|
||||
if (0 != (flags & UTF8_FLAG)) {
|
||||
u8length(in); // ignored
|
||||
int u8len = u8length(in);
|
||||
int start = in.position();
|
||||
int blength = u8len;
|
||||
while (in.get(start + blength) != 0) {
|
||||
blength++;
|
||||
}
|
||||
s = new String(in.array(), start, blength, "UTF-8");
|
||||
} else {
|
||||
int length = u16length(in);
|
||||
s = new String(in.array(), in.position(), length * 2, "UTF-16LE");
|
||||
}
|
||||
strings[i] = s;
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
static int u16length(ByteBuffer in) {
|
||||
int length = in.getShort() & 0xFFFF;
|
||||
if (length > 0x7FFF) {
|
||||
length = ((length & 0x7FFF) << 8) | (in.getShort() & 0xFFFF);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static int u8length(ByteBuffer in) {
|
||||
int len = in.get() & 0xFF;
|
||||
if ((len & 0x80) != 0) {
|
||||
len = ((len & 0x7F) << 8) | (in.get() & 0xFF);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
byte[] stringData;
|
||||
|
||||
public int getSize() {
|
||||
return 5 * 4 + this.size() * 4 + stringData.length + 0;// TODO
|
||||
}
|
||||
|
||||
public void prepare() throws IOException {
|
||||
for (StringItem s : this) {
|
||||
if (s.data.length() > 0x7FFF) {
|
||||
useUTF8 = false;
|
||||
}
|
||||
}
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int i = 0;
|
||||
int offset = 0;
|
||||
baos.reset();
|
||||
Map<String, Integer> map = new HashMap<String, Integer>();
|
||||
for (StringItem item : this) {
|
||||
item.index = i++;
|
||||
String stringData = item.data;
|
||||
Integer of = map.get(stringData);
|
||||
if (of != null) {
|
||||
item.dataOffset = of;
|
||||
} else {
|
||||
item.dataOffset = offset;
|
||||
map.put(stringData, offset);
|
||||
if (useUTF8) {
|
||||
int length = stringData.length();
|
||||
byte[] data = stringData.getBytes("UTF-8");
|
||||
int u8lenght = data.length;
|
||||
|
||||
if (length > 0x7F) {
|
||||
offset++;
|
||||
baos.write((length >> 8) | 0x80);
|
||||
}
|
||||
baos.write(length);
|
||||
|
||||
if (u8lenght > 0x7F) {
|
||||
offset++;
|
||||
baos.write((u8lenght >> 8) | 0x80);
|
||||
}
|
||||
baos.write(u8lenght);
|
||||
baos.write(data);
|
||||
baos.write(0);
|
||||
offset += 3 + u8lenght;
|
||||
} else {
|
||||
int length = stringData.length();
|
||||
byte[] data = stringData.getBytes("UTF-16LE");
|
||||
if (length > 0x7FFF) {
|
||||
int x = (length >> 16) | 0x8000;
|
||||
baos.write(x);
|
||||
baos.write(x >> 8);
|
||||
offset += 2;
|
||||
}
|
||||
baos.write(length);
|
||||
baos.write(length >> 8);
|
||||
baos.write(data);
|
||||
baos.write(0);
|
||||
baos.write(0);
|
||||
offset += 4 + data.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
stringData = baos.toByteArray();
|
||||
}
|
||||
|
||||
private boolean useUTF8 = true;
|
||||
|
||||
public void write(ByteBuffer out) throws IOException {
|
||||
out.putInt(this.size());
|
||||
out.putInt(0);// TODO style count
|
||||
out.putInt(useUTF8 ? UTF8_FLAG : 0);
|
||||
out.putInt(7 * 4 + this.size() * 4);
|
||||
out.putInt(0);
|
||||
for (StringItem item : this) {
|
||||
out.putInt(item.dataOffset);
|
||||
}
|
||||
out.put(stringData);
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.arsc;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import pxb.android.axml.Util;
|
||||
|
||||
/**
|
||||
* dump an arsc file
|
||||
*
|
||||
* @author bob
|
||||
*
|
||||
*/
|
||||
public class ArscDumper {
|
||||
public static void dump(List<Pkg> pkgs) {
|
||||
for (int x = 0; x < pkgs.size(); x++) {
|
||||
Pkg pkg = pkgs.get(x);
|
||||
|
||||
System.out.println(String.format(" Package %d id=%d name=%s typeCount=%d", x, pkg.id, pkg.name,
|
||||
pkg.types.size()));
|
||||
for (Type type : pkg.types.values()) {
|
||||
System.out.println(String.format(" type %d %s", type.id - 1, type.name));
|
||||
|
||||
int resPrefix = pkg.id << 24 | type.id << 16;
|
||||
for (int i = 0; i < type.specs.length; i++) {
|
||||
ResSpec spec = type.getSpec(i);
|
||||
System.out.println(String.format(" spec 0x%08x 0x%08x %s", resPrefix | spec.id, spec.flags,
|
||||
spec.name));
|
||||
}
|
||||
for (int i = 0; i < type.configs.size(); i++) {
|
||||
Config config = type.configs.get(i);
|
||||
System.out.println(" config");
|
||||
|
||||
List<ResEntry> entries = new ArrayList<ResEntry>(config.resources.values());
|
||||
for (int j = 0; j < entries.size(); j++) {
|
||||
ResEntry entry = entries.get(j);
|
||||
System.out.println(String.format(" resource 0x%08x %-20s: %s",
|
||||
resPrefix | entry.spec.id, entry.spec.name, entry.value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String... args) throws IOException {
|
||||
if (args.length == 0) {
|
||||
System.err.println("asrc-dump file.arsc");
|
||||
return;
|
||||
}
|
||||
byte[] data = Util.readFile(new File(args[0]));
|
||||
List<Pkg> pkgs = new ArscParser(data).parse();
|
||||
|
||||
dump(pkgs);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,317 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.arsc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import pxb.android.ResConst;
|
||||
import pxb.android.StringItems;
|
||||
|
||||
/**
|
||||
*
|
||||
* Read the resources.arsc inside an Android apk.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <pre>
|
||||
* byte[] oldArscFile= ... ; //
|
||||
* List<Pkg> pkgs = new ArscParser(oldArscFile).parse(); // read the file
|
||||
* modify(pkgs); // do what you want here
|
||||
* byte[] newArscFile = new ArscWriter(pkgs).toByteArray(); // build a new file
|
||||
* </pre>
|
||||
*
|
||||
* The format of arsc is described here (gingerbread)
|
||||
* <ul>
|
||||
* <li>frameworks/base/libs/utils/ResourceTypes.cpp</li>
|
||||
* <li>frameworks/base/include/utils/ResourceTypes.h</li>
|
||||
* </ul>
|
||||
* and the cmd line <code>aapt d resources abc.apk</code> is also good for debug
|
||||
* (available in android sdk)
|
||||
*
|
||||
* <p>
|
||||
* Todos:
|
||||
* <ul>
|
||||
* TODO add support to read styled strings
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Thanks to the the following projects
|
||||
* <ul>
|
||||
* <li>android4me https://code.google.com/p/android4me/</li>
|
||||
* <li>Apktool https://code.google.com/p/android-apktool</li>
|
||||
* <li>Android http://source.android.com/</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author bob
|
||||
*
|
||||
*/
|
||||
public class ArscParser implements ResConst {
|
||||
|
||||
/* pkg */class Chunk {
|
||||
|
||||
public final int headSize;
|
||||
public final int location;
|
||||
public final int size;
|
||||
public final int type;
|
||||
|
||||
public Chunk() {
|
||||
location = in.position();
|
||||
type = in.getShort() & 0xFFFF;
|
||||
headSize = in.getShort() & 0xFFFF;
|
||||
size = in.getInt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, this resource has been declared public, so libraries are allowed
|
||||
* to reference it.
|
||||
*/
|
||||
static final int ENGRY_FLAG_PUBLIC = 0x0002;
|
||||
|
||||
/**
|
||||
* If set, this is a complex entry, holding a set of name/value mappings. It
|
||||
* is followed by an array of ResTable_map structures.
|
||||
*/
|
||||
final static short ENTRY_FLAG_COMPLEX = 0x0001;
|
||||
public static final int TYPE_STRING = 0x03;
|
||||
|
||||
private int fileSize = -1;
|
||||
private ByteBuffer in;
|
||||
private String[] keyNamesX;
|
||||
private Pkg pkg;
|
||||
private List<Pkg> pkgs = new ArrayList<Pkg>();
|
||||
private String[] strings;
|
||||
private String[] typeNamesX;
|
||||
|
||||
public ArscParser(byte[] b) {
|
||||
this.in = ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
public List<Pkg> parse() throws IOException {
|
||||
if (fileSize < 0) {
|
||||
Chunk head = new Chunk();
|
||||
if (head.type != RES_TABLE_TYPE) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
fileSize = head.size;
|
||||
in.getInt();// packagecount
|
||||
}
|
||||
while (in.hasRemaining()) {
|
||||
Chunk chunk = new Chunk();
|
||||
switch (chunk.type) {
|
||||
case RES_STRING_POOL_TYPE:
|
||||
strings = StringItems.read(in);
|
||||
break;
|
||||
case RES_TABLE_PACKAGE_TYPE:
|
||||
readPackage(in);
|
||||
}
|
||||
in.position(chunk.location + chunk.size);
|
||||
}
|
||||
return pkgs;
|
||||
}
|
||||
|
||||
// private void readConfigFlags() {
|
||||
// int size = in.getInt();
|
||||
// if (size < 28) {
|
||||
// throw new RuntimeException();
|
||||
// }
|
||||
// short mcc = in.getShort();
|
||||
// short mnc = in.getShort();
|
||||
//
|
||||
// char[] language = new char[] { (char) in.get(), (char) in.get() };
|
||||
// char[] country = new char[] { (char) in.get(), (char) in.get() };
|
||||
//
|
||||
// byte orientation = in.get();
|
||||
// byte touchscreen = in.get();
|
||||
// short density = in.getShort();
|
||||
//
|
||||
// byte keyboard = in.get();
|
||||
// byte navigation = in.get();
|
||||
// byte inputFlags = in.get();
|
||||
// byte inputPad0 = in.get();
|
||||
//
|
||||
// short screenWidth = in.getShort();
|
||||
// short screenHeight = in.getShort();
|
||||
//
|
||||
// short sdkVersion = in.getShort();
|
||||
// short minorVersion = in.getShort();
|
||||
//
|
||||
// byte screenLayout = 0;
|
||||
// byte uiMode = 0;
|
||||
// short smallestScreenWidthDp = 0;
|
||||
// if (size >= 32) {
|
||||
// screenLayout = in.get();
|
||||
// uiMode = in.get();
|
||||
// smallestScreenWidthDp = in.getShort();
|
||||
// }
|
||||
//
|
||||
// short screenWidthDp = 0;
|
||||
// short screenHeightDp = 0;
|
||||
//
|
||||
// if (size >= 36) {
|
||||
// screenWidthDp = in.getShort();
|
||||
// screenHeightDp = in.getShort();
|
||||
// }
|
||||
//
|
||||
// short layoutDirection = 0;
|
||||
// if (size >= 38 && sdkVersion >= 17) {
|
||||
// layoutDirection = in.getShort();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
private void readEntry(Config config, ResSpec spec) {
|
||||
int size = in.getShort();
|
||||
|
||||
int flags = in.getShort(); // ENTRY_FLAG_PUBLIC
|
||||
int keyStr = in.getInt();
|
||||
spec.updateName(keyNamesX[keyStr]);
|
||||
|
||||
ResEntry resEntry = new ResEntry(flags, spec);
|
||||
|
||||
if (0 != (flags & ENTRY_FLAG_COMPLEX)) {
|
||||
|
||||
int parent = in.getInt();
|
||||
int count = in.getInt();
|
||||
BagValue bag = new BagValue(parent);
|
||||
for (int i = 0; i < count; i++) {
|
||||
Map.Entry<Integer, Value> entry = new AbstractMap.SimpleEntry(in.getInt(), readValue());
|
||||
bag.map.add(entry);
|
||||
}
|
||||
resEntry.value = bag;
|
||||
} else {
|
||||
resEntry.value = readValue();
|
||||
}
|
||||
config.resources.put(spec.id, resEntry);
|
||||
}
|
||||
|
||||
private void readPackage(ByteBuffer in) throws IOException {
|
||||
int pid = in.getInt() % 0xFF;
|
||||
|
||||
String name;
|
||||
{
|
||||
int nextPisition = in.position() + 128 * 2;
|
||||
StringBuilder sb = new StringBuilder(32);
|
||||
for (int i = 0; i < 128; i++) {
|
||||
int s = in.getShort();
|
||||
if (s == 0) {
|
||||
break;
|
||||
} else {
|
||||
sb.append((char) s);
|
||||
}
|
||||
}
|
||||
name = sb.toString();
|
||||
in.position(nextPisition);
|
||||
}
|
||||
|
||||
pkg = new Pkg(pid, name);
|
||||
pkgs.add(pkg);
|
||||
|
||||
int typeStringOff = in.getInt();
|
||||
int typeNameCount = in.getInt();
|
||||
int keyStringOff = in.getInt();
|
||||
int specNameCount = in.getInt();
|
||||
|
||||
{
|
||||
Chunk chunk = new Chunk();
|
||||
if (chunk.type != RES_STRING_POOL_TYPE) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
typeNamesX = StringItems.read(in);
|
||||
in.position(chunk.location + chunk.size);
|
||||
}
|
||||
{
|
||||
Chunk chunk = new Chunk();
|
||||
if (chunk.type != RES_STRING_POOL_TYPE) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
keyNamesX = StringItems.read(in);
|
||||
in.position(chunk.location + chunk.size);
|
||||
}
|
||||
|
||||
out: while (in.hasRemaining()) {
|
||||
Chunk chunk = new Chunk();
|
||||
switch (chunk.type) {
|
||||
case RES_TABLE_TYPE_SPEC_TYPE: {
|
||||
int tid = in.get() & 0xFF;
|
||||
in.get(); // res0
|
||||
in.getShort();// res1
|
||||
int entryCount = in.getInt();
|
||||
|
||||
Type t = pkg.getType(tid, typeNamesX[tid - 1], entryCount);
|
||||
for (int i = 0; i < entryCount; i++) {
|
||||
t.getSpec(i).flags = in.getInt();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RES_TABLE_TYPE_TYPE: {
|
||||
int tid = in.get() & 0xFF;
|
||||
in.get(); // res0
|
||||
in.getShort();// res1
|
||||
int entryCount = in.getInt();
|
||||
Type t = pkg.getType(tid, typeNamesX[tid - 1], entryCount);
|
||||
int entriesStart = in.getInt();
|
||||
|
||||
int p = in.position();
|
||||
int size = in.getInt();
|
||||
// readConfigFlags();
|
||||
byte[] data = new byte[size];
|
||||
in.position(p);
|
||||
in.get(data);
|
||||
Config config = new Config(data, entryCount);
|
||||
|
||||
in.position(chunk.location + chunk.headSize);
|
||||
|
||||
int[] entrys = new int[entryCount];
|
||||
for (int i = 0; i < entryCount; i++) {
|
||||
entrys[i] = in.getInt();
|
||||
}
|
||||
for (int i = 0; i < entrys.length; i++) {
|
||||
if (entrys[i] != -1) {
|
||||
in.position(chunk.location + entriesStart + entrys[i]);
|
||||
ResSpec spec = t.getSpec(i);
|
||||
readEntry(config, spec);
|
||||
}
|
||||
}
|
||||
|
||||
t.addConfig(config);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break out;
|
||||
}
|
||||
in.position(chunk.location + chunk.size);
|
||||
}
|
||||
}
|
||||
|
||||
private Object readValue() {
|
||||
int size1 = in.getShort();// 8
|
||||
int zero = in.get();// 0
|
||||
int type = in.get() & 0xFF; // TypedValue.*
|
||||
int data = in.getInt();
|
||||
String raw = null;
|
||||
if (type == TYPE_STRING) {
|
||||
raw = strings[data];
|
||||
}
|
||||
return new Value(type, data, raw);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,400 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.arsc;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import pxb.android.ResConst;
|
||||
import pxb.android.StringItem;
|
||||
import pxb.android.StringItems;
|
||||
import pxb.android.axml.Util;
|
||||
|
||||
/**
|
||||
* Write pkgs to an arsc file
|
||||
*
|
||||
* @see ArscParser
|
||||
* @author bob
|
||||
*
|
||||
*/
|
||||
public class ArscWriter implements ResConst {
|
||||
private static class PkgCtx {
|
||||
Map<String, StringItem> keyNames = new HashMap<String, StringItem>();
|
||||
StringItems keyNames0 = new StringItems();
|
||||
public int keyStringOff;
|
||||
int offset;
|
||||
Pkg pkg;
|
||||
int pkgSize;
|
||||
List<StringItem> typeNames = new ArrayList<StringItem>();
|
||||
|
||||
StringItems typeNames0 = new StringItems();
|
||||
int typeStringOff;
|
||||
|
||||
public void addKeyName(String name) {
|
||||
if (keyNames.containsKey(name)) {
|
||||
return;
|
||||
}
|
||||
StringItem stringItem = new StringItem(name);
|
||||
keyNames.put(name, stringItem);
|
||||
keyNames0.add(stringItem);
|
||||
}
|
||||
|
||||
public void addTypeName(int id, String name) {
|
||||
while (typeNames.size() <= id) {
|
||||
typeNames.add(null);
|
||||
}
|
||||
|
||||
StringItem item = typeNames.get(id);
|
||||
if (item == null) {
|
||||
typeNames.set(id, new StringItem(name));
|
||||
} else {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void D(String fmt, Object... args) {
|
||||
|
||||
}
|
||||
|
||||
private List<PkgCtx> ctxs = new ArrayList<PkgCtx>(5);
|
||||
private List<Pkg> pkgs;
|
||||
private Map<String, StringItem> strTable = new TreeMap<String, StringItem>();
|
||||
private StringItems strTable0 = new StringItems();
|
||||
|
||||
public ArscWriter(List<Pkg> pkgs) {
|
||||
this.pkgs = pkgs;
|
||||
}
|
||||
|
||||
public static void main(String... args) throws IOException {
|
||||
if (args.length < 2) {
|
||||
System.err.println("asrc-write-test in.arsc out.arsc");
|
||||
return;
|
||||
}
|
||||
byte[] data = Util.readFile(new File(args[0]));
|
||||
List<Pkg> pkgs = new ArscParser(data).parse();
|
||||
// ArscDumper.dump(pkgs);
|
||||
byte[] data2 = new ArscWriter(pkgs).toByteArray();
|
||||
// ArscDumper.dump(new ArscParser(data2).parse());
|
||||
Util.writeFile(data2, new File(args[1]));
|
||||
}
|
||||
|
||||
private void addString(String str) {
|
||||
if (strTable.containsKey(str)) {
|
||||
return;
|
||||
}
|
||||
StringItem stringItem = new StringItem(str);
|
||||
strTable.put(str, stringItem);
|
||||
strTable0.add(stringItem);
|
||||
}
|
||||
|
||||
private int count() {
|
||||
|
||||
int size = 0;
|
||||
|
||||
size += 8 + 4;// chunk, pkgcount
|
||||
{
|
||||
int stringSize = strTable0.getSize();
|
||||
if (stringSize % 4 != 0) {
|
||||
stringSize += 4 - stringSize % 4;
|
||||
}
|
||||
size += 8 + stringSize;// global strings
|
||||
}
|
||||
for (PkgCtx ctx : ctxs) {
|
||||
ctx.offset = size;
|
||||
int pkgSize = 0;
|
||||
pkgSize += 8 + 4 + 256;// chunk,pid+name
|
||||
pkgSize += 4 * 4;
|
||||
|
||||
ctx.typeStringOff = pkgSize;
|
||||
{
|
||||
int stringSize = ctx.typeNames0.getSize();
|
||||
if (stringSize % 4 != 0) {
|
||||
stringSize += 4 - stringSize % 4;
|
||||
}
|
||||
pkgSize += 8 + stringSize;// type names
|
||||
}
|
||||
|
||||
ctx.keyStringOff = pkgSize;
|
||||
|
||||
{
|
||||
int stringSize = ctx.keyNames0.getSize();
|
||||
if (stringSize % 4 != 0) {
|
||||
stringSize += 4 - stringSize % 4;
|
||||
}
|
||||
pkgSize += 8 + stringSize;// key names
|
||||
}
|
||||
|
||||
for (Type type : ctx.pkg.types.values()) {
|
||||
type.wPosition = size + pkgSize;
|
||||
pkgSize += 8 + 4 + 4 + 4 * type.specs.length; // trunk,id,entryCount,
|
||||
// configs
|
||||
|
||||
for (Config config : type.configs) {
|
||||
config.wPosition = pkgSize + size;
|
||||
int configBasePostion = pkgSize;
|
||||
pkgSize += 8 + 4 + 4 + 4; // trunk,id,entryCount,entriesStart
|
||||
int size0 = config.id.length;
|
||||
if (size0 % 4 != 0) {
|
||||
size0 += 4 - size0 % 4;
|
||||
}
|
||||
pkgSize += size0;// config
|
||||
|
||||
if (pkgSize - configBasePostion > 0x0038) {
|
||||
throw new RuntimeException("config id too big");
|
||||
} else {
|
||||
pkgSize = configBasePostion + 0x0038;
|
||||
}
|
||||
|
||||
pkgSize += 4 * config.entryCount;// offset
|
||||
config.wEntryStart = pkgSize - configBasePostion;
|
||||
int entryBase = pkgSize;
|
||||
for (ResEntry e : config.resources.values()) {
|
||||
e.wOffset = pkgSize - entryBase;
|
||||
pkgSize += 8;// size,flag,keyString
|
||||
if (e.value instanceof BagValue) {
|
||||
BagValue big = (BagValue) e.value;
|
||||
pkgSize += 8 + big.map.size() * 12;
|
||||
} else {
|
||||
pkgSize += 8;
|
||||
}
|
||||
}
|
||||
config.wChunkSize = pkgSize - configBasePostion;
|
||||
}
|
||||
}
|
||||
ctx.pkgSize = pkgSize;
|
||||
size += pkgSize;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
private List<PkgCtx> prepare() throws IOException {
|
||||
for (Pkg pkg : pkgs) {
|
||||
PkgCtx ctx = new PkgCtx();
|
||||
ctx.pkg = pkg;
|
||||
ctxs.add(ctx);
|
||||
|
||||
for (Type type : pkg.types.values()) {
|
||||
ctx.addTypeName(type.id - 1, type.name);
|
||||
for (ResSpec spec : type.specs) {
|
||||
ctx.addKeyName(spec.name);
|
||||
}
|
||||
for (Config config : type.configs) {
|
||||
for (ResEntry e : config.resources.values()) {
|
||||
Object object = e.value;
|
||||
if (object instanceof BagValue) {
|
||||
travelBagValue((BagValue) object);
|
||||
} else {
|
||||
travelValue((Value) object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.keyNames0.prepare();
|
||||
ctx.typeNames0.addAll(ctx.typeNames);
|
||||
ctx.typeNames0.prepare();
|
||||
}
|
||||
strTable0.prepare();
|
||||
return ctxs;
|
||||
}
|
||||
|
||||
public byte[] toByteArray() throws IOException {
|
||||
prepare();
|
||||
int size = count();
|
||||
ByteBuffer out = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
|
||||
write(out, size);
|
||||
return out.array();
|
||||
}
|
||||
|
||||
private void travelBagValue(BagValue bag) {
|
||||
for (Map.Entry<Integer, Value> e : bag.map) {
|
||||
travelValue(e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void travelValue(Value v) {
|
||||
if (v.raw != null) {
|
||||
addString(v.raw);
|
||||
}
|
||||
}
|
||||
|
||||
private void write(ByteBuffer out, int size) throws IOException {
|
||||
out.putInt(RES_TABLE_TYPE | (0x000c << 16));
|
||||
out.putInt(size);
|
||||
out.putInt(ctxs.size());
|
||||
|
||||
{
|
||||
int stringSize = strTable0.getSize();
|
||||
int padding = 0;
|
||||
if (stringSize % 4 != 0) {
|
||||
padding = 4 - stringSize % 4;
|
||||
}
|
||||
out.putInt(RES_STRING_POOL_TYPE | (0x001C << 16));
|
||||
out.putInt(stringSize + padding + 8);
|
||||
strTable0.write(out);
|
||||
out.put(new byte[padding]);
|
||||
}
|
||||
|
||||
for (PkgCtx pctx : ctxs) {
|
||||
if (out.position() != pctx.offset) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
final int basePosition = out.position();
|
||||
out.putInt(RES_TABLE_PACKAGE_TYPE | (0x011c << 16));
|
||||
out.putInt(pctx.pkgSize);
|
||||
out.putInt(pctx.pkg.id);
|
||||
int p = out.position();
|
||||
out.put(pctx.pkg.name.getBytes("UTF-16LE"));
|
||||
out.position(p + 256);
|
||||
|
||||
out.putInt(pctx.typeStringOff);
|
||||
out.putInt(pctx.typeNames0.size());
|
||||
|
||||
out.putInt(pctx.keyStringOff);
|
||||
out.putInt(pctx.keyNames0.size());
|
||||
|
||||
{
|
||||
if (out.position() - basePosition != pctx.typeStringOff) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
int stringSize = pctx.typeNames0.getSize();
|
||||
int padding = 0;
|
||||
if (stringSize % 4 != 0) {
|
||||
padding = 4 - stringSize % 4;
|
||||
}
|
||||
out.putInt(RES_STRING_POOL_TYPE | (0x001C << 16));
|
||||
out.putInt(stringSize + padding + 8);
|
||||
pctx.typeNames0.write(out);
|
||||
out.put(new byte[padding]);
|
||||
}
|
||||
|
||||
{
|
||||
if (out.position() - basePosition != pctx.keyStringOff) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
int stringSize = pctx.keyNames0.getSize();
|
||||
int padding = 0;
|
||||
if (stringSize % 4 != 0) {
|
||||
padding = 4 - stringSize % 4;
|
||||
}
|
||||
out.putInt(RES_STRING_POOL_TYPE | (0x001C << 16));
|
||||
out.putInt(stringSize + padding + 8);
|
||||
pctx.keyNames0.write(out);
|
||||
out.put(new byte[padding]);
|
||||
}
|
||||
|
||||
for (Type t : pctx.pkg.types.values()) {
|
||||
D("[%08x]write spec", out.position(), t.name);
|
||||
if (t.wPosition != out.position()) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
out.putInt(RES_TABLE_TYPE_SPEC_TYPE | (0x0010 << 16));
|
||||
out.putInt(4 * 4 + 4 * t.specs.length);// size
|
||||
|
||||
out.putInt(t.id);
|
||||
out.putInt(t.specs.length);
|
||||
for (ResSpec spec : t.specs) {
|
||||
out.putInt(spec.flags);
|
||||
}
|
||||
|
||||
for (Config config : t.configs) {
|
||||
D("[%08x]write config", out.position());
|
||||
int typeConfigPosition = out.position();
|
||||
if (config.wPosition != typeConfigPosition) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
out.putInt(RES_TABLE_TYPE_TYPE | (0x0038 << 16));
|
||||
out.putInt(config.wChunkSize);// size
|
||||
|
||||
out.putInt(t.id);
|
||||
out.putInt(t.specs.length);
|
||||
out.putInt(config.wEntryStart);
|
||||
|
||||
D("[%08x]write config ids", out.position());
|
||||
out.put(config.id);
|
||||
|
||||
int size0 = config.id.length;
|
||||
int padding = 0;
|
||||
if (size0 % 4 != 0) {
|
||||
padding = 4 - size0 % 4;
|
||||
}
|
||||
out.put(new byte[padding]);
|
||||
|
||||
out.position(typeConfigPosition + 0x0038);
|
||||
|
||||
D("[%08x]write config entry offsets", out.position());
|
||||
for (int i = 0; i < config.entryCount; i++) {
|
||||
ResEntry entry = config.resources.get(i);
|
||||
if (entry == null) {
|
||||
out.putInt(-1);
|
||||
} else {
|
||||
out.putInt(entry.wOffset);
|
||||
}
|
||||
}
|
||||
|
||||
if (out.position() - typeConfigPosition != config.wEntryStart) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
D("[%08x]write config entrys", out.position());
|
||||
for (ResEntry e : config.resources.values()) {
|
||||
D("[%08x]ResTable_entry", out.position());
|
||||
boolean isBag = e.value instanceof BagValue;
|
||||
out.putShort((short) (isBag ? 16 : 8));
|
||||
int flag = e.flag;
|
||||
if (isBag) { // add complex flag
|
||||
flag |= ArscParser.ENTRY_FLAG_COMPLEX;
|
||||
} else { // remove
|
||||
flag &= ~ArscParser.ENTRY_FLAG_COMPLEX;
|
||||
}
|
||||
out.putShort((short) flag);
|
||||
out.putInt(pctx.keyNames.get(e.spec.name).index);
|
||||
if (isBag) {
|
||||
BagValue bag = (BagValue) e.value;
|
||||
out.putInt(bag.parent);
|
||||
out.putInt(bag.map.size());
|
||||
for (Map.Entry<Integer, Value> entry : bag.map) {
|
||||
out.putInt(entry.getKey());
|
||||
writeValue(entry.getValue(), out);
|
||||
}
|
||||
} else {
|
||||
writeValue((Value) e.value, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeValue(Value value, ByteBuffer out) {
|
||||
out.putShort((short) 8);
|
||||
out.put((byte) 0);
|
||||
out.put((byte) value.type);
|
||||
if (value.type == ArscParser.TYPE_STRING) {
|
||||
out.putInt(strTable.get(value.raw).index);
|
||||
} else {
|
||||
out.putInt(value.data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.arsc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class BagValue {
|
||||
public List<Map.Entry<Integer, Value>> map = new ArrayList<Entry<Integer, Value>>();
|
||||
public final int parent;
|
||||
|
||||
public BagValue(int parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (!(obj instanceof BagValue))
|
||||
return false;
|
||||
BagValue other = (BagValue) obj;
|
||||
if (map == null) {
|
||||
if (other.map != null)
|
||||
return false;
|
||||
} else if (!map.equals(other.map))
|
||||
return false;
|
||||
if (parent != other.parent)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((map == null) ? 0 : map.hashCode());
|
||||
result = prime * result + parent;
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("{bag%08x", parent));
|
||||
for (Map.Entry<Integer, Value> e : map) {
|
||||
sb.append(",").append(String.format("0x%08x", e.getKey()));
|
||||
sb.append("=");
|
||||
sb.append(e.getValue());
|
||||
}
|
||||
|
||||
return sb.append("}").toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.arsc;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class Config {
|
||||
public final int entryCount;
|
||||
public final byte[] id;
|
||||
public Map<Integer, ResEntry> resources = new TreeMap<Integer, ResEntry>();
|
||||
/* package */int wChunkSize;
|
||||
/* package */int wEntryStart;
|
||||
/* package */int wPosition;
|
||||
|
||||
public Config(byte[] id, int entryCount) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.entryCount = entryCount;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.arsc;
|
||||
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class Pkg {
|
||||
public final int id;
|
||||
public String name;
|
||||
public TreeMap<Integer, Type> types = new TreeMap<Integer, Type>();
|
||||
|
||||
public Pkg(int id, String name) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Type getType(int tid, String name, int entrySize) {
|
||||
Type type = types.get(tid);
|
||||
if (type != null) {
|
||||
if (name != null) {
|
||||
if (type.name == null) {
|
||||
type.name = name;
|
||||
} else if (!name.endsWith(type.name)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (type.specs.length != entrySize) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
type = new Type();
|
||||
type.id = tid;
|
||||
type.name = name;
|
||||
type.specs = new ResSpec[entrySize];
|
||||
types.put(tid, type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.arsc;
|
||||
|
||||
public class ResEntry {
|
||||
public final int flag;
|
||||
|
||||
public final ResSpec spec;
|
||||
/**
|
||||
* {@link BagValue} or {@link Value}
|
||||
*/
|
||||
public Object value;
|
||||
|
||||
/* package */int wOffset;
|
||||
|
||||
public ResEntry(int flag, ResSpec spec) {
|
||||
super();
|
||||
this.flag = flag;
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.arsc;
|
||||
|
||||
public class ResSpec {
|
||||
public int flags;
|
||||
public final int id;
|
||||
public String name;
|
||||
|
||||
public ResSpec(int id) {
|
||||
super();
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void updateName(String s) {
|
||||
String name = this.name;
|
||||
if (name == null) {
|
||||
this.name = s;
|
||||
} else if (!s.equals(name)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.arsc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Type {
|
||||
public List<Config> configs = new ArrayList<Config>();
|
||||
public int id;
|
||||
public String name;
|
||||
public ResSpec[] specs;
|
||||
/* package */int wPosition;
|
||||
|
||||
public void addConfig(Config config) {
|
||||
if (config.entryCount != specs.length) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
configs.add(config);
|
||||
}
|
||||
|
||||
public ResSpec getSpec(int resId) {
|
||||
ResSpec res = specs[resId];
|
||||
if (res == null) {
|
||||
res = new ResSpec(resId);
|
||||
specs[resId] = res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.arsc;
|
||||
|
||||
public class Value {
|
||||
public final int data;
|
||||
public String raw;
|
||||
public final int type;
|
||||
|
||||
public Value(int type, int data, String raw) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (type == 0x03) {
|
||||
return raw;
|
||||
}
|
||||
return String.format("{t=0x%02x d=0x%08x}", type, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.axml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Axml extends AxmlVisitor {
|
||||
|
||||
public static class Node extends NodeVisitor {
|
||||
public static class Attr {
|
||||
public String ns, name;
|
||||
public int resourceId, type;
|
||||
public Object value;
|
||||
|
||||
public void accept(NodeVisitor nodeVisitor) {
|
||||
nodeVisitor.attr(ns, name, resourceId, type, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Text {
|
||||
public int ln;
|
||||
public String text;
|
||||
|
||||
public void accept(NodeVisitor nodeVisitor) {
|
||||
nodeVisitor.text(ln, text);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Attr> attrs = new ArrayList<Attr>();
|
||||
public List<Node> children = new ArrayList<Node>();
|
||||
public Integer ln;
|
||||
public String ns, name;
|
||||
public Text text;
|
||||
|
||||
public void accept(NodeVisitor nodeVisitor) {
|
||||
NodeVisitor nodeVisitor2 = nodeVisitor.child(ns, name);
|
||||
acceptB(nodeVisitor2);
|
||||
nodeVisitor2.end();
|
||||
}
|
||||
|
||||
public void acceptB(NodeVisitor nodeVisitor) {
|
||||
if (text != null) {
|
||||
text.accept(nodeVisitor);
|
||||
}
|
||||
for (Attr a : attrs) {
|
||||
a.accept(nodeVisitor);
|
||||
}
|
||||
if (ln != null) {
|
||||
nodeVisitor.line(ln);
|
||||
}
|
||||
for (Node c : children) {
|
||||
c.accept(nodeVisitor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attr(String ns, String name, int resourceId, int type, Object obj) {
|
||||
Attr attr = new Attr();
|
||||
attr.name = name;
|
||||
attr.ns = ns;
|
||||
attr.resourceId = resourceId;
|
||||
attr.type = type;
|
||||
attr.value = obj;
|
||||
attrs.add(attr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeVisitor child(String ns, String name) {
|
||||
Node node = new Node();
|
||||
node.name = name;
|
||||
node.ns = ns;
|
||||
children.add(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void line(int ln) {
|
||||
this.ln = ln;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void text(int lineNumber, String value) {
|
||||
Text text = new Text();
|
||||
text.ln = lineNumber;
|
||||
text.text = value;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Ns {
|
||||
public int ln;
|
||||
public String prefix, uri;
|
||||
|
||||
public void accept(AxmlVisitor visitor) {
|
||||
visitor.ns(prefix, uri, ln);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Node> firsts = new ArrayList<Node>();
|
||||
public List<Ns> nses = new ArrayList<Ns>();
|
||||
|
||||
public void accept(final AxmlVisitor visitor) {
|
||||
for (Ns ns : nses) {
|
||||
ns.accept(visitor);
|
||||
}
|
||||
for (Node first : firsts) {
|
||||
first.accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeVisitor child(String ns, String name) {
|
||||
Node node = new Node();
|
||||
node.name = name;
|
||||
node.ns = ns;
|
||||
firsts.add(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ns(String prefix, String uri, int ln) {
|
||||
Ns ns = new Ns();
|
||||
ns.prefix = prefix;
|
||||
ns.uri = uri;
|
||||
ns.ln = ln;
|
||||
nses.add(ns);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,274 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.axml;
|
||||
|
||||
import static pxb.android.axml.NodeVisitor.TYPE_INT_BOOLEAN;
|
||||
import static pxb.android.axml.NodeVisitor.TYPE_STRING;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import pxb.android.ResConst;
|
||||
import pxb.android.StringItems;
|
||||
|
||||
/**
|
||||
* a class to read android axml
|
||||
*
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
*/
|
||||
public class AxmlParser implements ResConst {
|
||||
|
||||
public static final int END_FILE = 7;
|
||||
public static final int END_NS = 5;
|
||||
public static final int END_TAG = 3;
|
||||
public static final int START_FILE = 1;
|
||||
public static final int START_NS = 4;
|
||||
public static final int START_TAG = 2;
|
||||
public static final int TEXT = 6;
|
||||
// private int attrName[];
|
||||
// private int attrNs[];
|
||||
// private int attrResId[];
|
||||
// private int attrType[];
|
||||
// private Object attrValue[];
|
||||
|
||||
private int attributeCount;
|
||||
|
||||
private IntBuffer attrs;
|
||||
|
||||
private int classAttribute;
|
||||
private int fileSize = -1;
|
||||
private int idAttribute;
|
||||
private ByteBuffer in;
|
||||
private int lineNumber;
|
||||
private int nameIdx;
|
||||
private int nsIdx;
|
||||
|
||||
private int prefixIdx;
|
||||
|
||||
private int[] resourceIds;
|
||||
|
||||
private String[] strings;
|
||||
|
||||
private int styleAttribute;
|
||||
|
||||
private int textIdx;
|
||||
|
||||
public AxmlParser(byte[] data) {
|
||||
this(ByteBuffer.wrap(data));
|
||||
}
|
||||
|
||||
public AxmlParser(ByteBuffer in) {
|
||||
super();
|
||||
this.in = in.order(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
public int getAttrCount() {
|
||||
return attributeCount;
|
||||
}
|
||||
|
||||
public int getAttributeCount() {
|
||||
return attributeCount;
|
||||
}
|
||||
|
||||
public String getAttrName(int i) {
|
||||
int idx = attrs.get(i * 5 + 1);
|
||||
return strings[idx];
|
||||
|
||||
}
|
||||
|
||||
public String getAttrNs(int i) {
|
||||
int idx = attrs.get(i * 5 + 0);
|
||||
return idx >= 0 ? strings[idx] : null;
|
||||
}
|
||||
|
||||
String getAttrRawString(int i) {
|
||||
int idx = attrs.get(i * 5 + 2);
|
||||
if (idx >= 0) {
|
||||
return strings[idx];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getAttrResId(int i) {
|
||||
if (resourceIds != null) {
|
||||
int idx = attrs.get(i * 5 + 1);
|
||||
if (idx >= 0 && idx < resourceIds.length) {
|
||||
return resourceIds[idx];
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getAttrType(int i) {
|
||||
return attrs.get(i * 5 + 3) >> 24;
|
||||
}
|
||||
|
||||
public Object getAttrValue(int i) {
|
||||
int v = attrs.get(i * 5 + 4);
|
||||
|
||||
if (i == idAttribute) {
|
||||
return ValueWrapper.wrapId(v, getAttrRawString(i));
|
||||
} else if (i == styleAttribute) {
|
||||
return ValueWrapper.wrapStyle(v, getAttrRawString(i));
|
||||
} else if (i == classAttribute) {
|
||||
return ValueWrapper.wrapClass(v, getAttrRawString(i));
|
||||
}
|
||||
|
||||
switch (getAttrType(i)) {
|
||||
case TYPE_STRING:
|
||||
return strings[v];
|
||||
case TYPE_INT_BOOLEAN:
|
||||
return v != 0;
|
||||
default:
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return strings[nameIdx];
|
||||
}
|
||||
|
||||
public String getNamespacePrefix() {
|
||||
return strings[prefixIdx];
|
||||
}
|
||||
|
||||
public String getNamespaceUri() {
|
||||
return nsIdx >= 0 ? strings[nsIdx] : null;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return strings[textIdx];
|
||||
}
|
||||
|
||||
public int next() throws IOException {
|
||||
if (fileSize < 0) {
|
||||
int type = in.getInt() & 0xFFFF;
|
||||
if (type != RES_XML_TYPE) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
fileSize = in.getInt();
|
||||
return START_FILE;
|
||||
}
|
||||
int event = -1;
|
||||
for (int p = in.position(); p < fileSize; p = in.position()) {
|
||||
int type = in.getInt() & 0xFFFF;
|
||||
int size = in.getInt();
|
||||
switch (type) {
|
||||
case RES_XML_START_ELEMENT_TYPE: {
|
||||
{
|
||||
lineNumber = in.getInt();
|
||||
in.getInt();/* skip, 0xFFFFFFFF */
|
||||
nsIdx = in.getInt();
|
||||
nameIdx = in.getInt();
|
||||
int flag = in.getInt();// 0x00140014 ?
|
||||
if (flag != 0x00140014) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
attributeCount = in.getShort() & 0xFFFF;
|
||||
idAttribute = (in.getShort() & 0xFFFF) - 1;
|
||||
classAttribute = (in.getShort() & 0xFFFF) - 1;
|
||||
styleAttribute = (in.getShort() & 0xFFFF) - 1;
|
||||
|
||||
attrs = in.asIntBuffer();
|
||||
|
||||
// attrResId = new int[attributeCount];
|
||||
// attrName = new int[attributeCount];
|
||||
// attrNs = new int[attributeCount];
|
||||
// attrType = new int[attributeCount];
|
||||
// attrValue = new Object[attributeCount];
|
||||
// for (int i = 0; i < attributeCount; i++) {
|
||||
// int attrNsIdx = in.getInt();
|
||||
// int attrNameIdx = in.getInt();
|
||||
// int raw = in.getInt();
|
||||
// int aValueType = in.getInt() >>> 24;
|
||||
// int aValue = in.getInt();
|
||||
// Object value = null;
|
||||
// switch (aValueType) {
|
||||
// case TYPE_STRING:
|
||||
// value = strings[aValue];
|
||||
// break;
|
||||
// case TYPE_INT_BOOLEAN:
|
||||
// value = aValue != 0;
|
||||
// break;
|
||||
// default:
|
||||
// value = aValue;
|
||||
// }
|
||||
// int resourceId = attrNameIdx < this.resourceIds.length ?
|
||||
// resourceIds[attrNameIdx] : -1;
|
||||
// attrNs[i] = attrNsIdx;
|
||||
// attrName[i] = attrNameIdx;
|
||||
// attrType[i] = aValueType;
|
||||
// attrResId[i] = resourceId;
|
||||
// attrValue[i] = value;
|
||||
// }
|
||||
event = START_TAG;
|
||||
}
|
||||
break;
|
||||
case RES_XML_END_ELEMENT_TYPE: {
|
||||
in.position(p + size);
|
||||
event = END_TAG;
|
||||
}
|
||||
break;
|
||||
case RES_XML_START_NAMESPACE_TYPE:
|
||||
lineNumber = in.getInt();
|
||||
in.getInt();/* 0xFFFFFFFF */
|
||||
prefixIdx = in.getInt();
|
||||
nsIdx = in.getInt();
|
||||
event = START_NS;
|
||||
break;
|
||||
case RES_XML_END_NAMESPACE_TYPE:
|
||||
in.position(p + size);
|
||||
event = END_NS;
|
||||
break;
|
||||
case RES_STRING_POOL_TYPE:
|
||||
strings = StringItems.read(in);
|
||||
in.position(p + size);
|
||||
continue;
|
||||
case RES_XML_RESOURCE_MAP_TYPE:
|
||||
int count = size / 4 - 2;
|
||||
resourceIds = new int[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
resourceIds[i] = in.getInt();
|
||||
}
|
||||
in.position(p + size);
|
||||
continue;
|
||||
case RES_XML_CDATA_TYPE:
|
||||
lineNumber = in.getInt();
|
||||
in.getInt();/* 0xFFFFFFFF */
|
||||
textIdx = in.getInt();
|
||||
|
||||
in.getInt();/* 00000008 00000000 */
|
||||
in.getInt();
|
||||
|
||||
event = TEXT;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unsupported type: " + type);
|
||||
}
|
||||
in.position(p + size);
|
||||
return event;
|
||||
}
|
||||
return END_FILE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.axml;
|
||||
|
||||
import static pxb.android.axml.AxmlParser.END_FILE;
|
||||
import static pxb.android.axml.AxmlParser.END_NS;
|
||||
import static pxb.android.axml.AxmlParser.END_TAG;
|
||||
import static pxb.android.axml.AxmlParser.START_FILE;
|
||||
import static pxb.android.axml.AxmlParser.START_NS;
|
||||
import static pxb.android.axml.AxmlParser.START_TAG;
|
||||
import static pxb.android.axml.AxmlParser.TEXT;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Stack;
|
||||
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
|
||||
/**
|
||||
* a class to read android axml
|
||||
*
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
*/
|
||||
public class AxmlReader {
|
||||
public static final NodeVisitor EMPTY_VISITOR = new NodeVisitor() {
|
||||
|
||||
@Override
|
||||
public NodeVisitor child(String ns, String name) {
|
||||
return this;
|
||||
}
|
||||
|
||||
};
|
||||
final AxmlParser parser;
|
||||
|
||||
public AxmlReader(byte[] data) {
|
||||
super();
|
||||
this.parser = new AxmlParser(data);
|
||||
}
|
||||
|
||||
public void accept(final AxmlVisitor av) throws IOException {
|
||||
Stack<NodeVisitor> nvs = new Stack<NodeVisitor>();
|
||||
NodeVisitor tos = av;
|
||||
while (true) {
|
||||
int type = parser.next();
|
||||
switch (type) {
|
||||
case START_FILE:
|
||||
break;
|
||||
case START_TAG:
|
||||
nvs.push(tos);
|
||||
tos = tos.child(parser.getNamespaceUri(), parser.getName());
|
||||
if (tos != null) {
|
||||
if (tos != EMPTY_VISITOR) {
|
||||
tos.line(parser.getLineNumber());
|
||||
for (int i = 0; i < parser.getAttrCount(); i++) {
|
||||
tos.attr(parser.getAttrNs(i), parser.getAttrName(i), parser.getAttrResId(i),
|
||||
parser.getAttrType(i), parser.getAttrValue(i));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tos = EMPTY_VISITOR;
|
||||
}
|
||||
break;
|
||||
case END_TAG:
|
||||
tos.end();
|
||||
tos = nvs.pop();
|
||||
break;
|
||||
case START_NS:
|
||||
av.ns(parser.getNamespacePrefix(), parser.getNamespaceUri(), parser.getLineNumber());
|
||||
break;
|
||||
case END_NS:
|
||||
break;
|
||||
case TEXT:
|
||||
tos.text(parser.getLineNumber(), parser.getText());
|
||||
break;
|
||||
case END_FILE:
|
||||
return;
|
||||
default:
|
||||
XposedBridge.log("Unsupported tag: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.axml;
|
||||
|
||||
/**
|
||||
* visitor to visit an axml
|
||||
*
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
*/
|
||||
public class AxmlVisitor extends NodeVisitor {
|
||||
|
||||
public AxmlVisitor() {
|
||||
super();
|
||||
|
||||
}
|
||||
|
||||
public AxmlVisitor(NodeVisitor av) {
|
||||
super(av);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a ns
|
||||
*
|
||||
* @param prefix
|
||||
* @param uri
|
||||
* @param ln
|
||||
*/
|
||||
public void ns(String prefix, String uri, int ln) {
|
||||
if (nv != null && nv instanceof AxmlVisitor) {
|
||||
((AxmlVisitor) nv).ns(prefix, uri, ln);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,442 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.axml;
|
||||
|
||||
import static pxb.android.ResConst.RES_STRING_POOL_TYPE;
|
||||
import static pxb.android.ResConst.RES_XML_CDATA_TYPE;
|
||||
import static pxb.android.ResConst.RES_XML_END_ELEMENT_TYPE;
|
||||
import static pxb.android.ResConst.RES_XML_END_NAMESPACE_TYPE;
|
||||
import static pxb.android.ResConst.RES_XML_RESOURCE_MAP_TYPE;
|
||||
import static pxb.android.ResConst.RES_XML_START_ELEMENT_TYPE;
|
||||
import static pxb.android.ResConst.RES_XML_START_NAMESPACE_TYPE;
|
||||
import static pxb.android.ResConst.RES_XML_TYPE;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import pxb.android.StringItem;
|
||||
import pxb.android.StringItems;
|
||||
|
||||
/**
|
||||
* a class to write android axml
|
||||
*
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
*/
|
||||
public class AxmlWriter extends AxmlVisitor {
|
||||
static final Comparator<Attr> ATTR_CMP = new Comparator<Attr>() {
|
||||
|
||||
@Override
|
||||
public int compare(Attr a, Attr b) {
|
||||
int x = a.resourceId - b.resourceId;
|
||||
if (x == 0) {
|
||||
x = a.name.data.compareTo(b.name.data);
|
||||
if (x == 0) {
|
||||
boolean aNsIsnull = a.ns == null;
|
||||
boolean bNsIsnull = b.ns == null;
|
||||
if (aNsIsnull) {
|
||||
if (bNsIsnull) {
|
||||
x = 0;
|
||||
} else {
|
||||
x = -1;
|
||||
}
|
||||
} else {
|
||||
if (bNsIsnull) {
|
||||
x = 1;
|
||||
} else {
|
||||
x = a.ns.data.compareTo(b.ns.data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
static class Attr {
|
||||
|
||||
public int index;
|
||||
public StringItem name;
|
||||
public StringItem ns;
|
||||
public int resourceId;
|
||||
public int type;
|
||||
public Object value;
|
||||
public StringItem raw;
|
||||
|
||||
public Attr(StringItem ns, StringItem name, int resourceId) {
|
||||
super();
|
||||
this.ns = ns;
|
||||
this.name = name;
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public void prepare(AxmlWriter axmlWriter) {
|
||||
ns = axmlWriter.updateNs(ns);
|
||||
if (this.name != null) {
|
||||
if (resourceId != -1) {
|
||||
this.name = axmlWriter.updateWithResourceId(this.name, this.resourceId);
|
||||
} else {
|
||||
this.name = axmlWriter.update(this.name);
|
||||
}
|
||||
}
|
||||
if (value instanceof StringItem) {
|
||||
value = axmlWriter.update((StringItem) value);
|
||||
}
|
||||
if (raw != null) {
|
||||
raw = axmlWriter.update(raw);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NodeImpl extends NodeVisitor {
|
||||
private Set<Attr> attrs = new TreeSet<Attr>(ATTR_CMP);
|
||||
private List<NodeImpl> children = new ArrayList<NodeImpl>();
|
||||
private int line;
|
||||
private StringItem name;
|
||||
private StringItem ns;
|
||||
private StringItem text;
|
||||
private int textLineNumber;
|
||||
Attr id;
|
||||
Attr style;
|
||||
Attr clz;
|
||||
|
||||
public NodeImpl(String ns, String name) {
|
||||
super(null);
|
||||
this.ns = ns == null ? null : new StringItem(ns);
|
||||
this.name = name == null ? null : new StringItem(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attr(String ns, String name, int resourceId, int type, Object value) {
|
||||
if (name == null) {
|
||||
throw new RuntimeException("name can't be null");
|
||||
}
|
||||
Attr a = new Attr(ns == null ? null : new StringItem(ns), new StringItem(name), resourceId);
|
||||
a.type = type;
|
||||
|
||||
if (value instanceof ValueWrapper) {
|
||||
ValueWrapper valueWrapper = (ValueWrapper) value;
|
||||
if (valueWrapper.raw != null) {
|
||||
a.raw = new StringItem(valueWrapper.raw);
|
||||
}
|
||||
a.value = valueWrapper.ref;
|
||||
switch (valueWrapper.type) {
|
||||
case ValueWrapper.CLASS:
|
||||
clz = a;
|
||||
break;
|
||||
case ValueWrapper.ID:
|
||||
id = a;
|
||||
break;
|
||||
case ValueWrapper.STYLE:
|
||||
style = a;
|
||||
break;
|
||||
}
|
||||
} else if (type == TYPE_STRING) {
|
||||
StringItem raw = new StringItem((String) value);
|
||||
a.raw = raw;
|
||||
a.value = raw;
|
||||
|
||||
} else {
|
||||
a.raw = null;
|
||||
a.value = value;
|
||||
}
|
||||
|
||||
attrs.add(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeVisitor child(String ns, String name) {
|
||||
NodeImpl child = new NodeImpl(ns, name);
|
||||
this.children.add(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void line(int ln) {
|
||||
this.line = ln;
|
||||
}
|
||||
|
||||
public int prepare(AxmlWriter axmlWriter) {
|
||||
ns = axmlWriter.updateNs(ns);
|
||||
name = axmlWriter.update(name);
|
||||
|
||||
int attrIndex = 0;
|
||||
for (Attr attr : attrs) {
|
||||
attr.index = attrIndex++;
|
||||
attr.prepare(axmlWriter);
|
||||
}
|
||||
|
||||
text = axmlWriter.update(text);
|
||||
int size = 24 + 36 + attrs.size() * 20;// 24 for end tag,36+x*20 for
|
||||
// start tag
|
||||
for (NodeImpl child : children) {
|
||||
size += child.prepare(axmlWriter);
|
||||
}
|
||||
if (text != null) {
|
||||
size += 28;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void text(int ln, String value) {
|
||||
this.text = new StringItem(value);
|
||||
this.textLineNumber = ln;
|
||||
}
|
||||
|
||||
void write(ByteBuffer out) throws IOException {
|
||||
// start tag
|
||||
out.putInt(RES_XML_START_ELEMENT_TYPE | (0x0010 << 16));
|
||||
out.putInt(36 + attrs.size() * 20);
|
||||
out.putInt(line);
|
||||
out.putInt(0xFFFFFFFF);
|
||||
out.putInt(ns != null ? this.ns.index : -1);
|
||||
out.putInt(name.index);
|
||||
out.putInt(0x00140014);// TODO
|
||||
out.putShort((short) this.attrs.size());
|
||||
out.putShort((short) (id == null ? 0 : id.index + 1));
|
||||
out.putShort((short) (clz == null ? 0 : clz.index + 1));
|
||||
out.putShort((short) (style == null ? 0 : style.index + 1));
|
||||
for (Attr attr : attrs) {
|
||||
out.putInt(attr.ns == null ? -1 : attr.ns.index);
|
||||
out.putInt(attr.name.index);
|
||||
out.putInt(attr.raw != null ? attr.raw.index : -1);
|
||||
out.putInt((attr.type << 24) | 0x000008);
|
||||
Object v = attr.value;
|
||||
if (v instanceof StringItem) {
|
||||
out.putInt(((StringItem) attr.value).index);
|
||||
} else if (v instanceof Boolean) {
|
||||
out.putInt(Boolean.TRUE.equals(v) ? -1 : 0);
|
||||
} else if (v instanceof Float) {
|
||||
out.putInt(Float.floatToIntBits((float) v));
|
||||
} else {
|
||||
out.putInt((Integer) attr.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.text != null) {
|
||||
out.putInt(RES_XML_CDATA_TYPE | (0x0010 << 16));
|
||||
out.putInt(28);
|
||||
out.putInt(textLineNumber);
|
||||
out.putInt(0xFFFFFFFF);
|
||||
out.putInt(text.index);
|
||||
out.putInt(0x00000008);
|
||||
out.putInt(0x00000000);
|
||||
}
|
||||
|
||||
// children
|
||||
for (NodeImpl child : children) {
|
||||
child.write(out);
|
||||
}
|
||||
|
||||
// end tag
|
||||
out.putInt(RES_XML_END_ELEMENT_TYPE | (0x0010 << 16));
|
||||
out.putInt(24);
|
||||
out.putInt(-1);
|
||||
out.putInt(0xFFFFFFFF);
|
||||
out.putInt(ns != null ? this.ns.index : -1);
|
||||
out.putInt(name.index);
|
||||
}
|
||||
}
|
||||
|
||||
static class Ns {
|
||||
int ln;
|
||||
StringItem prefix;
|
||||
StringItem uri;
|
||||
|
||||
public Ns(StringItem prefix, StringItem uri, int ln) {
|
||||
super();
|
||||
this.prefix = prefix;
|
||||
this.uri = uri;
|
||||
this.ln = ln;
|
||||
}
|
||||
}
|
||||
|
||||
private List<NodeImpl> firsts = new ArrayList<NodeImpl>(3);
|
||||
|
||||
private Map<String, Ns> nses = new HashMap<String, Ns>();
|
||||
|
||||
private List<StringItem> otherString = new ArrayList<StringItem>();
|
||||
|
||||
private Map<String, StringItem> resourceId2Str = new HashMap<String, StringItem>();
|
||||
|
||||
private List<Integer> resourceIds = new ArrayList<Integer>();
|
||||
|
||||
private List<StringItem> resourceString = new ArrayList<StringItem>();
|
||||
|
||||
private StringItems stringItems = new StringItems();
|
||||
|
||||
// TODO add style support
|
||||
// private List<StringItem> styleItems = new ArrayList();
|
||||
|
||||
@Override
|
||||
public NodeVisitor child(String ns, String name) {
|
||||
NodeImpl first = new NodeImpl(ns, name);
|
||||
this.firsts.add(first);
|
||||
return first;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ns(String prefix, String uri, int ln) {
|
||||
nses.put(uri, new Ns(prefix == null ? null : new StringItem(prefix), new StringItem(uri), ln));
|
||||
}
|
||||
|
||||
private int prepare() throws IOException {
|
||||
int size = 0;
|
||||
|
||||
for (NodeImpl first : firsts) {
|
||||
size += first.prepare(this);
|
||||
}
|
||||
{
|
||||
int a = 0;
|
||||
for (Map.Entry<String, Ns> e : nses.entrySet()) {
|
||||
Ns ns = e.getValue();
|
||||
if (ns == null) {
|
||||
ns = new Ns(null, new StringItem(e.getKey()), 0);
|
||||
e.setValue(ns);
|
||||
}
|
||||
if (ns.prefix == null) {
|
||||
ns.prefix = new StringItem(String.format("axml_auto_%02d", a++));
|
||||
}
|
||||
ns.prefix = update(ns.prefix);
|
||||
ns.uri = update(ns.uri);
|
||||
}
|
||||
}
|
||||
|
||||
size += nses.size() * 24 * 2;
|
||||
|
||||
this.stringItems.addAll(resourceString);
|
||||
resourceString = null;
|
||||
this.stringItems.addAll(otherString);
|
||||
otherString = null;
|
||||
this.stringItems.prepare();
|
||||
int stringSize = this.stringItems.getSize();
|
||||
if (stringSize % 4 != 0) {
|
||||
stringSize += 4 - stringSize % 4;
|
||||
}
|
||||
size += 8 + stringSize;
|
||||
size += 8 + resourceIds.size() * 4;
|
||||
return size;
|
||||
}
|
||||
|
||||
public byte[] toByteArray() throws IOException {
|
||||
|
||||
int size = 8 + prepare();
|
||||
ByteBuffer out = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
out.putInt(RES_XML_TYPE | (0x0008 << 16));
|
||||
out.putInt(size);
|
||||
|
||||
int stringSize = this.stringItems.getSize();
|
||||
int padding = 0;
|
||||
if (stringSize % 4 != 0) {
|
||||
padding = 4 - stringSize % 4;
|
||||
}
|
||||
out.putInt(RES_STRING_POOL_TYPE | (0x001C << 16));
|
||||
out.putInt(stringSize + padding + 8);
|
||||
this.stringItems.write(out);
|
||||
out.put(new byte[padding]);
|
||||
|
||||
out.putInt(RES_XML_RESOURCE_MAP_TYPE | (0x0008 << 16));
|
||||
out.putInt(8 + this.resourceIds.size() * 4);
|
||||
for (Integer i : resourceIds) {
|
||||
out.putInt(i);
|
||||
}
|
||||
|
||||
Stack<Ns> stack = new Stack<Ns>();
|
||||
for (Map.Entry<String, Ns> e : this.nses.entrySet()) {
|
||||
Ns ns = e.getValue();
|
||||
stack.push(ns);
|
||||
out.putInt(RES_XML_START_NAMESPACE_TYPE | (0x0010 << 16));
|
||||
out.putInt(24);
|
||||
out.putInt(-1);
|
||||
out.putInt(0xFFFFFFFF);
|
||||
out.putInt(ns.prefix.index);
|
||||
out.putInt(ns.uri.index);
|
||||
}
|
||||
|
||||
for (NodeImpl first : firsts) {
|
||||
first.write(out);
|
||||
}
|
||||
|
||||
while (stack.size() > 0) {
|
||||
Ns ns = stack.pop();
|
||||
out.putInt(RES_XML_END_NAMESPACE_TYPE | (0x0010 << 16));
|
||||
out.putInt(24);
|
||||
out.putInt(ns.ln);
|
||||
out.putInt(0xFFFFFFFF);
|
||||
out.putInt(ns.prefix.index);
|
||||
out.putInt(ns.uri.index);
|
||||
}
|
||||
return out.array();
|
||||
}
|
||||
|
||||
StringItem update(StringItem item) {
|
||||
if (item == null)
|
||||
return null;
|
||||
int i = this.otherString.indexOf(item);
|
||||
if (i < 0) {
|
||||
StringItem copy = new StringItem(item.data);
|
||||
this.otherString.add(copy);
|
||||
return copy;
|
||||
} else {
|
||||
return this.otherString.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
StringItem updateNs(StringItem item) {
|
||||
if (item == null) {
|
||||
return null;
|
||||
}
|
||||
String ns = item.data;
|
||||
if (!this.nses.containsKey(ns)) {
|
||||
this.nses.put(ns, null);
|
||||
}
|
||||
return update(item);
|
||||
}
|
||||
|
||||
StringItem updateWithResourceId(StringItem name, int resourceId) {
|
||||
String key = name.data + resourceId;
|
||||
StringItem item = this.resourceId2Str.get(key);
|
||||
if (item != null) {
|
||||
return item;
|
||||
} else {
|
||||
StringItem copy = new StringItem(name.data);
|
||||
resourceIds.add(resourceId);
|
||||
resourceString.add(copy);
|
||||
resourceId2Str.put(key, copy);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.axml;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* dump axml to stdout
|
||||
*
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
*/
|
||||
public class DumpAdapter extends AxmlVisitor {
|
||||
protected int deep;
|
||||
protected Map<String, String> nses;
|
||||
|
||||
public DumpAdapter() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public DumpAdapter(NodeVisitor nv) {
|
||||
this(nv, 0, new HashMap<String, String>());
|
||||
}
|
||||
|
||||
public DumpAdapter(NodeVisitor nv, int x, Map<String, String> nses) {
|
||||
super(nv);
|
||||
this.deep = x;
|
||||
this.nses = nses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attr(String ns, String name, int resourceId, int type, Object obj) {
|
||||
for (int i = 0; i < deep; i++) {
|
||||
System.out.print(" ");
|
||||
}
|
||||
if (ns != null) {
|
||||
System.out.print(String.format("%s:", getPrefix(ns)));
|
||||
}
|
||||
System.out.print(name);
|
||||
if (resourceId != -1) {
|
||||
System.out.print(String.format("(%08x)", resourceId));
|
||||
}
|
||||
if (obj instanceof String) {
|
||||
System.out.print(String.format("=[%08x]\"%s\"", type, obj));
|
||||
} else if (obj instanceof Boolean) {
|
||||
System.out.print(String.format("=[%08x]\"%b\"", type, obj));
|
||||
} else if (obj instanceof ValueWrapper) {
|
||||
ValueWrapper w = (ValueWrapper) obj;
|
||||
System.out.print(String.format("=[%08x]@%08x, raw: \"%s\"", type, w.ref, w.raw));
|
||||
} else if (type == TYPE_REFERENCE) {
|
||||
System.out.print(String.format("=[%08x]@%08x", type, obj));
|
||||
} else {
|
||||
System.out.print(String.format("=[%08x]%08x", type, obj));
|
||||
}
|
||||
System.out.println();
|
||||
super.attr(ns, name, resourceId, type, obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeVisitor child(String ns, String name) {
|
||||
for (int i = 0; i < deep; i++) {
|
||||
System.out.print(" ");
|
||||
}
|
||||
System.out.print("<");
|
||||
if (ns != null) {
|
||||
System.out.print(getPrefix(ns) + ":");
|
||||
}
|
||||
System.out.println(name);
|
||||
NodeVisitor nv = super.child(ns, name);
|
||||
if (nv != null) {
|
||||
return new DumpAdapter(nv, deep + 1, nses);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String getPrefix(String uri) {
|
||||
if (nses != null) {
|
||||
String prefix = nses.get(uri);
|
||||
if (prefix != null) {
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ns(String prefix, String uri, int ln) {
|
||||
System.out.println(prefix + "=" + uri);
|
||||
this.nses.put(uri, prefix);
|
||||
super.ns(prefix, uri, ln);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void text(int ln, String value) {
|
||||
for (int i = 0; i < deep + 1; i++) {
|
||||
System.out.print(" ");
|
||||
}
|
||||
System.out.print("T: ");
|
||||
System.out.println(value);
|
||||
super.text(ln, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.axml;
|
||||
|
||||
public abstract class NodeVisitor {
|
||||
|
||||
public static final int TYPE_FIRST_INT = 0x10;
|
||||
public static final int TYPE_INT_BOOLEAN = 0x12;
|
||||
public static final int TYPE_INT_HEX = 0x11;
|
||||
public static final int TYPE_REFERENCE = 0x01;
|
||||
public static final int TYPE_STRING = 0x03;
|
||||
protected NodeVisitor nv;
|
||||
|
||||
public NodeVisitor() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NodeVisitor(NodeVisitor nv) {
|
||||
super();
|
||||
this.nv = nv;
|
||||
}
|
||||
|
||||
/**
|
||||
* add attribute to the node
|
||||
*
|
||||
* @param ns
|
||||
* @param name
|
||||
* @param resourceId
|
||||
* @param type
|
||||
* {@link #TYPE_STRING} or others
|
||||
* @param obj
|
||||
* a string for {@link #TYPE_STRING} ,and Integer for others
|
||||
*/
|
||||
public void attr(String ns, String name, int resourceId, int type, Object obj) {
|
||||
if (nv != null) {
|
||||
nv.attr(ns, name, resourceId, type, obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create a child node
|
||||
*
|
||||
* @param ns
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public NodeVisitor child(String ns, String name) {
|
||||
if (nv != null) {
|
||||
return nv.child(ns, name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* end the visit
|
||||
*/
|
||||
public void end() {
|
||||
if (nv != null) {
|
||||
nv.end();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* line number in the .xml
|
||||
*
|
||||
* @param ln
|
||||
*/
|
||||
public void line(int ln) {
|
||||
if (nv != null) {
|
||||
nv.line(ln);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* the node text
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
public void text(int lineNumber, String value) {
|
||||
if (nv != null) {
|
||||
nv.text(lineNumber, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2013 Panxiaobo
|
||||
*
|
||||
* 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 pxb.android.axml;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Util {
|
||||
public static byte[] readFile(File in) throws IOException {
|
||||
InputStream is = new FileInputStream(in);
|
||||
byte[] xml = new byte[is.available()];
|
||||
is.read(xml);
|
||||
is.close();
|
||||
return xml;
|
||||
}
|
||||
|
||||
public static byte[] readIs(InputStream is) throws IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
copy(is, os);
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
public static void writeFile(byte[] data, File out) throws IOException {
|
||||
FileOutputStream fos = new FileOutputStream(out);
|
||||
fos.write(data);
|
||||
fos.close();
|
||||
}
|
||||
|
||||
public static Map<String, String> readProguardConfig(File config) throws IOException {
|
||||
Map<String, String> clzMap = new HashMap<String, String>();
|
||||
BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(config), "utf8"));
|
||||
try {
|
||||
for (String ln = r.readLine(); ln != null; ln = r.readLine()) {
|
||||
if (ln.startsWith("#") || ln.startsWith(" ")) {
|
||||
continue;
|
||||
}
|
||||
// format a.pt.Main -> a.a.a:
|
||||
int i = ln.indexOf("->");
|
||||
if (i > 0) {
|
||||
clzMap.put(ln.substring(0, i).trim(), ln.substring(i + 2, ln.length() - 1).trim());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
r.close();
|
||||
}
|
||||
return clzMap;
|
||||
}
|
||||
|
||||
public static void copy(InputStream is, OutputStream os) throws IOException {
|
||||
byte[] xml = new byte[10 * 1024];
|
||||
for (int c = is.read(xml); c > 0; c = is.read(xml)) {
|
||||
os.write(xml, 0, c);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package pxb.android.axml;
|
||||
|
||||
public class ValueWrapper {
|
||||
|
||||
public static final int ID = 1;
|
||||
public static final int STYLE = 2;
|
||||
public static final int CLASS = 3;
|
||||
public final int type;
|
||||
public final String raw;
|
||||
public final int ref;
|
||||
|
||||
private ValueWrapper(int type, int ref, String raw) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.raw = raw;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
public ValueWrapper replaceRaw(String raw) {
|
||||
return new ValueWrapper(type, ref, raw);
|
||||
}
|
||||
|
||||
public static ValueWrapper wrapId(int ref, String raw) {
|
||||
return new ValueWrapper(ID, ref, raw);
|
||||
}
|
||||
|
||||
public static ValueWrapper wrapStyle(int ref, String raw) {
|
||||
return new ValueWrapper(STYLE, ref, raw);
|
||||
}
|
||||
|
||||
public static ValueWrapper wrapClass(int ref, String raw) {
|
||||
return new ValueWrapper(CLASS, ref, raw);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
<p>Source code of <a href="https://code.google.com/p/axml/">axml project</a>.</p>
|
||||
<p>Revision 2394d02d86bb - Apr 22, 2014</p>
|
||||
Loading…
Reference in New Issue