Use RePlugin's own reflection library to replace Apache Lang3

This commit is contained in:
zhangjiongxuan 2017-07-17 21:20:37 +08:00
parent d3ef61a51a
commit ed2c5dceac
71 changed files with 65 additions and 53150 deletions

View File

@ -23,11 +23,7 @@ import android.os.Build;
import android.util.DisplayMetrics;
import com.qihoo360.replugin.helper.LogDebug;
import com.qihoo360.replugin.ext.lang3.ClassUtils;
import com.qihoo360.replugin.ext.lang3.reflect.ConstructorUtils;
import com.qihoo360.replugin.ext.lang3.reflect.FieldUtils;
import com.qihoo360.replugin.ext.lang3.reflect.MethodUtils;
import com.qihoo360.replugin.utils.ReflectUtils;
import java.io.File;
@ -47,13 +43,15 @@ public class PackageUtils {
//
try {
// 1. 新建PackageParser的实例
Object packageParser = ConstructorUtils.invokeConstructor(ClassUtils.getClass("android.content.pm.PackageParser"), archiveFilePath);
Object packageParser = ReflectUtils.invokeConstructor(ReflectUtils.getClass("android.content.pm.PackageParser"),
new Class[]{String.class}, archiveFilePath);
// 2. 调用PackageParser.parsePackage()方法返回值为Package对象
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
Object pkg = MethodUtils.invokeMethod(packageParser, true, "parsePackage", new File(archiveFilePath), archiveFilePath, metrics, 0);
Object pkg = ReflectUtils.invokeMethod(packageParser, "parsePackage", new Class[]{File.class, String.class, DisplayMetrics.class, int.class},
new File(archiveFilePath), archiveFilePath, metrics, 0);
if (pkg == null) {
if (LOG) {
LogDebug.d(MISC_TAG, "failed to parsePackage: f=" + archiveFilePath);
@ -62,13 +60,14 @@ public class PackageUtils {
}
// 3. 调用PackageParser.collectCertificates方法
boolean rc = (Boolean) MethodUtils.invokeMethod(packageParser, "collectCertificates", pkg, 0);
boolean rc = (Boolean) ReflectUtils.invokeMethod(packageParser, "collectCertificates", new Class[]{pkg.getClass(), int.class},
pkg, 0);
if (!rc) {
return null;
}
// 4. 获取Package.mSignatures
Object signatures[] = (Object[]) FieldUtils.readField(pkg, "mSignatures");
Object signatures[] = (Object[]) ReflectUtils.readField(pkg, "mSignatures");
int n = signatures.length;
if (n <= 0) {
if (LOG) {

View File

@ -22,8 +22,7 @@ import android.util.Log;
import com.qihoo360.replugin.RePlugin;
import com.qihoo360.replugin.helper.LogRelease;
import com.qihoo360.replugin.ext.lang3.reflect.FieldUtils;
import com.qihoo360.replugin.utils.ReflectUtils;
import static com.qihoo360.replugin.helper.LogDebug.LOG;
import static com.qihoo360.replugin.helper.LogDebug.PLUGIN_TAG;
@ -53,7 +52,7 @@ public class PatchClassLoaderUtils {
// 1. ApplicationContext - Android 2.1
// 2. ContextImpl - Android 2.2 and higher
// 3. AppContextImpl - Android 2.2 and higher
Object oPackageInfo = FieldUtils.readField(oBase, "mPackageInfo", true);
Object oPackageInfo = ReflectUtils.readField(oBase, "mPackageInfo");
if (oPackageInfo == null) {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "pclu.p: nf mpi. mb cl=" + oBase.getClass());
@ -69,7 +68,7 @@ public class PatchClassLoaderUtils {
}
// 获取mPackageInfo.mClassLoader
ClassLoader oClassLoader = (ClassLoader) FieldUtils.readField(oPackageInfo, "mClassLoader", true);
ClassLoader oClassLoader = (ClassLoader) ReflectUtils.readField(oPackageInfo, "mClassLoader");
if (oClassLoader == null) {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "pclu.p: nf mpi. mb cl=" + oBase.getClass() + "; mpi cl=" + oPackageInfo.getClass());
@ -81,7 +80,7 @@ public class PatchClassLoaderUtils {
ClassLoader cl = RePlugin.getConfig().getCallbacks().createClassLoader(oClassLoader.getParent(), oClassLoader);
// 将新的ClassLoader写入mPackageInfo.mClassLoader
FieldUtils.writeField(oPackageInfo, "mClassLoader", cl, true);
ReflectUtils.writeField(oPackageInfo, "mClassLoader", cl);
if (LOG) {
Log.d(TAG, "patch: patch mClassLoader ok");

View File

@ -1,108 +0,0 @@
/*
* Copyright (C) 2005-2017 Qihoo 360 Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed To in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.qihoo360.loader.utils;
import android.content.Context;
import com.qihoo360.replugin.helper.LogRelease;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static com.qihoo360.replugin.helper.LogDebug.MISC_TAG;
import static com.qihoo360.replugin.helper.LogRelease.LOGR;
/**
* @author RePlugin Team
*/
public final class ReflectUtils {
public static final void setFieldNonE(Class<?> c, Object object, String fName, Object value) {
try {
setField(c, object, fName, value);
} catch (Throwable e) {
e.printStackTrace();
}
}
public static final void setField(Class<?> c, Object object, String fName, Object value) throws NoSuchFieldException, IllegalAccessException {
Field f = c.getDeclaredField(fName);
boolean acc = f.isAccessible();
if (!acc) {
f.setAccessible(true);
}
f.set(object, value);
if (!acc) {
f.setAccessible(acc);
}
}
public static final void dumpObject(Object object, FileDescriptor fd, PrintWriter writer, String[] args) {
try {
Class<?> c = object.getClass();
do {
writer.println("c=" + c.getName());
Field fields[] = c.getDeclaredFields();
for (Field f : fields) {
boolean acc = f.isAccessible();
if (!acc) {
f.setAccessible(true);
}
Object o = f.get(object);
writer.print(f.getName());
writer.print("=");
if (o != null) {
writer.println(o.toString());
} else {
writer.println("null");
}
if (!acc) {
f.setAccessible(acc);
}
}
c = c.getSuperclass();
} while (c != null && !c.equals(Object.class) && !c.equals(Context.class));
} catch (Throwable e) {
if (LOGR) {
LogRelease.e(MISC_TAG, e.getMessage(), e);
}
}
}
public static Object invokeMethod(ClassLoader loader, String clzName,
String methodName, Object methodReceiver,
Class<?>[] methodParamTypes, Object... methodParamValues) throws
ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
if (methodReceiver == null) {
return null;
}
Class clz = Class.forName(clzName, false, loader);
if (clz != null) {
Method med = clz.getMethod(methodName, methodParamTypes);
if (med != null) {
med.setAccessible(true);
return med.invoke(methodReceiver, methodParamValues);
}
}
return null;
}
}

View File

@ -16,8 +16,7 @@
package com.qihoo360.loader.utils2;
import com.qihoo360.replugin.ext.lang3.ClassUtils;
import com.qihoo360.replugin.ext.lang3.reflect.MethodUtils;
import com.qihoo360.replugin.utils.ReflectUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@ -58,7 +57,7 @@ public class FilePermissionUtils {
try {
initClass();
if (sSetPermissionMethod == null) {
sSetPermissionMethod = MethodUtils.getMatchingMethod(sFileUtilsClass, "setPermissions",
sSetPermissionMethod = ReflectUtils.getMethod(sFileUtilsClass, "setPermissions",
String.class, Integer.TYPE, Integer.TYPE, Integer.TYPE);
}
Object retObj = sSetPermissionMethod.invoke(null, filePath, mode, uid, gid);
@ -85,7 +84,7 @@ public class FilePermissionUtils {
try {
initClass();
if (sGetPermissionMethod == null) {
sGetPermissionMethod = MethodUtils.getMatchingMethod(sFileUtilsClass, "getPermissions",
sGetPermissionMethod = ReflectUtils.getMethod(sFileUtilsClass, "getPermissions",
String.class, int[].class);
}
Object retObj = sGetPermissionMethod.invoke(null, filePath, outPermissions);
@ -104,7 +103,7 @@ public class FilePermissionUtils {
private static void initClass() throws ClassNotFoundException {
if (sFileUtilsClass == null) {
sFileUtilsClass = ClassUtils.getClass("android.os.FileUtils");
sFileUtilsClass = ReflectUtils.getClass("android.os.FileUtils");
}
}
}

View File

@ -33,9 +33,8 @@ import com.qihoo360.i.Factory;
import com.qihoo360.i.IModule;
import com.qihoo360.i.IPluginActivityManager;
import com.qihoo360.i.IPluginManager;
import com.qihoo360.loader.utils.ReflectUtils;
import com.qihoo360.replugin.utils.ReflectUtils;
import com.qihoo360.mobilesafe.api.Tasks;
import com.qihoo360.mobilesafe.core.BuildConfig;
import com.qihoo360.replugin.IHostBinderFetcher;
import com.qihoo360.replugin.RePlugin;
import com.qihoo360.replugin.RePluginConstants;

View File

@ -29,12 +29,10 @@ import com.qihoo360.i.Factory;
import com.qihoo360.i.Factory2;
import com.qihoo360.i.IPluginActivityManager;
import com.qihoo360.i.IPluginManager;
import com.qihoo360.loader.utils.ReflectUtils;
import com.qihoo360.replugin.utils.ReflectUtils;
import com.qihoo360.replugin.RePlugin;
import com.qihoo360.replugin.base.IPC;
import com.qihoo360.replugin.component.activity.ActivityInjector;
import com.qihoo360.replugin.ext.lang3.ClassUtils;
import com.qihoo360.replugin.ext.lang3.reflect.FieldUtils;
import com.qihoo360.replugin.helper.HostConfigHelper;
import com.qihoo360.replugin.helper.LogDebug;
import com.qihoo360.replugin.helper.LogRelease;
@ -680,12 +678,14 @@ class PmInternalImpl implements IPluginActivityManager {
private static int getDefaultThemeId() {
if (HostConfigHelper.ACTIVITY_PIT_USE_APPCOMPAT) {
try {
Class clazz = ClassUtils.getClass("android.support.v7.appcompat.R$style");
return (int) FieldUtils.readStaticField(clazz, "Theme_AppCompat", true);
Class clazz = ReflectUtils.getClass("android.support.v7.appcompat.R$style");
return (int) ReflectUtils.readStaticField(clazz, "Theme_AppCompat");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
return android.R.style.Theme_NoTitleBar;

View File

@ -16,10 +16,9 @@
package com.qihoo360.replugin;
import com.qihoo360.replugin.utils.ReflectUtils;
import com.qihoo360.replugin.helper.LogDebug;
import com.qihoo360.replugin.ext.lang3.reflect.MethodUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@ -63,7 +62,7 @@ public class PluginDexClassLoader extends DexClassLoader {
private static void initMethods(ClassLoader cl) {
Class<?> clz = cl.getClass();
if (sLoadClassMethod == null) {
sLoadClassMethod = MethodUtils.getMatchingMethod(clz, "loadClass", String.class, Boolean.TYPE);
sLoadClassMethod = ReflectUtils.getMethod(clz, "loadClass", String.class, Boolean.TYPE);
if (sLoadClassMethod == null) {
throw new NoSuchMethodError("loadClass");
}

View File

@ -18,15 +18,13 @@ package com.qihoo360.replugin;
import android.os.Build;
import com.qihoo360.replugin.utils.ReflectUtils;
import com.qihoo360.loader.utils.StringUtils;
import com.qihoo360.loader2.PMF;
import com.qihoo360.replugin.base.IPC;
import com.qihoo360.replugin.helper.LogDebug;
import com.qihoo360.replugin.helper.LogRelease;
import com.qihoo360.replugin.ext.lang3.reflect.FieldUtils;
import com.qihoo360.replugin.ext.lang3.reflect.MethodUtils;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@ -83,19 +81,19 @@ public class RePluginClassLoader extends PathClassLoader {
private void initMethods(ClassLoader cl) {
Class<?> c = cl.getClass();
findResourceMethod = MethodUtils.getMatchingMethod(c, "findResource", String.class);
findResourceMethod = ReflectUtils.getMethod(c, "findResource", String.class);
findResourceMethod.setAccessible(true);
findResourcesMethod = MethodUtils.getMatchingMethod(c, "findResources", String.class);
findResourcesMethod = ReflectUtils.getMethod(c, "findResources", String.class);
findResourcesMethod.setAccessible(true);
findLibraryMethod = MethodUtils.getMatchingMethod(c, "findLibrary", String.class);
findLibraryMethod = ReflectUtils.getMethod(c, "findLibrary", String.class);
findLibraryMethod.setAccessible(true);
getPackageMethod = MethodUtils.getMatchingMethod(c, "getPackage", String.class);
getPackageMethod = ReflectUtils.getMethod(c, "getPackage", String.class);
getPackageMethod.setAccessible(true);
}
private void copyFromOriginal(ClassLoader orig) {
if (LOG && IPC.isPersistentProcess()) {
LogDebug.d(TAG, "copyFromOriginal: Fields=" + StringUtils.toStringWithLines(FieldUtils.getAllFieldsList(orig.getClass())));
LogDebug.d(TAG, "copyFromOriginal: Fields=" + StringUtils.toStringWithLines(ReflectUtils.getAllFieldsList(orig.getClass())));
}
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
@ -116,7 +114,7 @@ public class RePluginClassLoader extends PathClassLoader {
private void copyFieldValue(String field, ClassLoader orig) {
try {
Field f = FieldUtils.getField(orig.getClass(), field, true);
Field f = ReflectUtils.getField(orig.getClass(), field);
if (f == null) {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "rpcl.cfv: null! f=" + field);
@ -125,14 +123,14 @@ public class RePluginClassLoader extends PathClassLoader {
}
// 删除final修饰符
FieldUtils.removeFinalModifier(f);
ReflectUtils.removeFieldFinalModifier(f);
// 复制Field中的值到this里
Object o = FieldUtils.readField(f, orig);
FieldUtils.writeField(f, this, o);
Object o = ReflectUtils.readField(f, orig);
ReflectUtils.writeField(f, this, o);
if (LOG) {
Object test = FieldUtils.readField(f, this);
Object test = ReflectUtils.readField(f, this);
LogDebug.d(TAG, "copyFieldValue: Copied. f=" + field + "; actually=" + test + "; orig=" + o);
}
} catch (IllegalAccessException e) {

View File

@ -1,306 +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 com.qihoo360.replugin.ext.lang3;
/**
* <p>Operations on {@link CharSequence} that are
* {@code null} safe.</p>
*
* @see CharSequence
* @since 3.0
*/
public class CharSequenceUtils {
private static final int NOT_FOUND = -1;
/**
* <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(final CharSequence cs, final int start) {
return cs == null ? null : cs.subSequence(start, cs.length());
}
//-----------------------------------------------------------------------
/**
* Returns the index within <code>cs</code> of the first occurrence of the
* specified character, starting the search at the specified index.
* <p>
* If a character with value <code>searchChar</code> occurs in the
* character sequence represented by the <code>cs</code>
* object at an index no smaller than <code>start</code>, then
* the index of the first such occurrence is returned. For values
* of <code>searchChar</code> in the range from 0 to 0xFFFF (inclusive),
* this is the smallest value <i>k</i> such that:
* <blockquote><pre>
* (this.charAt(<i>k</i>) == searchChar) &amp;&amp; (<i>k</i> &gt;= start)
* </pre></blockquote>
* is true. For other values of <code>searchChar</code>, it is the
* smallest value <i>k</i> such that:
* <blockquote><pre>
* (this.codePointAt(<i>k</i>) == searchChar) &amp;&amp; (<i>k</i> &gt;= start)
* </pre></blockquote>
* is true. In either case, if no such character occurs inm <code>cs</code>
* at or after position <code>start</code>, then
* <code>-1</code> is returned.
*
* <p>
* There is no restriction on the value of <code>start</code>. If it
* is negative, it has the same effect as if it were zero: the entire
* <code>CharSequence</code> may be searched. If it is greater than
* the length of <code>cs</code>, it has the same effect as if it were
* equal to the length of <code>cs</code>: <code>-1</code> is returned.
*
* <p>All indices are specified in <code>char</code> values
* (Unicode code units).
*
* @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
* @since 3.6 updated to behave more like <code>String</code>
*/
static int indexOf(final CharSequence cs, final int searchChar, int start) {
if (cs instanceof String) {
return ((String) cs).indexOf(searchChar, start);
}
final int sz = cs.length();
if (start < 0) {
start = 0;
}
if (searchChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
for (int i = start; i < sz; i++) {
if (cs.charAt(i) == searchChar) {
return i;
}
}
}
//supplementary characters (LANG1300)
if (searchChar <= Character.MAX_CODE_POINT) {
char[] chars = Character.toChars(searchChar);
for (int i = start; i < sz - 1; i++) {
char high = cs.charAt(i);
char low = cs.charAt(i + 1);
if (high == chars[0] && low == chars[1]) {
return i;
}
}
}
return NOT_FOUND;
}
/**
* 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(final CharSequence cs, final CharSequence searchChar, final 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);
// }
}
/**
* Returns the index within <code>cs</code> of the last occurrence of
* the specified character, searching backward starting at the
* specified index. For values of <code>searchChar</code> in the range
* from 0 to 0xFFFF (inclusive), the index returned is the largest
* value <i>k</i> such that:
* <blockquote><pre>
* (this.charAt(<i>k</i>) == searchChar) &amp;&amp; (<i>k</i> &lt;= start)
* </pre></blockquote>
* is true. For other values of <code>searchChar</code>, it is the
* largest value <i>k</i> such that:
* <blockquote><pre>
* (this.codePointAt(<i>k</i>) == searchChar) &amp;&amp; (<i>k</i> &lt;= start)
* </pre></blockquote>
* is true. In either case, if no such character occurs in <code>cs</code>
* at or before position <code>start</code>, then <code>-1</code> is returned.
*
* <p>All indices are specified in <code>char</code> values
* (Unicode code units).
*
* @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
* @since 3.6 updated to behave more like <code>String</code>
*/
static int lastIndexOf(final CharSequence cs, final int searchChar, int start) {
if (cs instanceof String) {
return ((String) cs).lastIndexOf(searchChar, start);
}
final int sz = cs.length();
if (start < 0) {
return NOT_FOUND;
}
if (start >= sz) {
start = sz - 1;
}
if (searchChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
for (int i = start; i >= 0; --i) {
if (cs.charAt(i) == searchChar) {
return i;
}
}
}
//supplementary characters (LANG1300)
//NOTE - we must do a forward traversal for this to avoid duplicating code points
if (searchChar <= Character.MAX_CODE_POINT) {
char[] chars = Character.toChars(searchChar);
//make sure it's not the last index
if (start == sz - 1) {
return NOT_FOUND;
}
for (int i = start; i >= 0; i--) {
char high = cs.charAt(i);
char low = cs.charAt(i + 1);
if (chars[0] == high && chars[1] == low) {
return i;
}
}
}
return NOT_FOUND;
}
/**
* 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(final CharSequence cs, final CharSequence searchChar, final 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(final CharSequence cs) {
if (cs instanceof String) {
return ((String) cs).toCharArray();
}
final int sz = cs.length();
final 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(final CharSequence cs, final boolean ignoreCase, final int thisStart,
final CharSequence substring, final int start, final int length) {
if (cs instanceof String && substring instanceof String) {
return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length);
}
int index1 = thisStart;
int index2 = start;
int tmpLen = length;
// Extract these first so we detect NPEs the same as the java.lang.String version
final int srcLen = cs.length() - thisStart;
final int otherLen = substring.length() - start;
// Check for invalid parameters
if (thisStart < 0 || start < 0 || length < 0) {
return false;
}
// Check that the regions are long enough
if (srcLen < length || otherLen < length) {
return false;
}
while (tmpLen-- > 0) {
final char c1 = cs.charAt(index1++);
final char c2 = substring.charAt(index2++);
if (c1 == c2) {
continue;
}
if (!ignoreCase) {
return false;
}
// The same check as in String.regionMatches():
if (Character.toUpperCase(c1) != Character.toUpperCase(c2)
&& Character.toLowerCase(c1) != Character.toLowerCase(c2)) {
return false;
}
}
return true;
}
}

View File

@ -1,552 +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 com.qihoo360.replugin.ext.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
*/
public class CharUtils {
private static final String[] CHAR_STRING_ARRAY = new String[128];
private static final char[] HEX_DIGITS = new char[] {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
/**
* {@code \u000a} linefeed LF ('\n').
*
* @see <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6">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://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6">JLF: Escape Sequences
* for Character and String Literals</a>
* @since 2.2
*/
public static final char CR = '\r';
/**
* {@code \u0000} null control character ('\0'), abbreviated NUL.
*
* @since 3.6
*/
public static final char NUL = '\0';
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(final 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(final 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(final Character ch) {
Validate.isTrue(ch != null, "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(final Character ch, final 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(final String str) {
Validate.isTrue(StringUtils.isNotEmpty(str), "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(final String str, final 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(final 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(final char ch, final 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(final Character ch) {
Validate.isTrue(ch != null, "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(final Character ch, final 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(final 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(final 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(final char ch) {
final StringBuilder sb = new StringBuilder(6);
sb.append("\\u");
sb.append(HEX_DIGITS[(ch >> 12) & 15]);
sb.append(HEX_DIGITS[(ch >> 8) & 15]);
sb.append(HEX_DIGITS[(ch >> 4) & 15]);
sb.append(HEX_DIGITS[(ch) & 15]);
return sb.toString();
}
/**
* <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(final 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('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if less than 128
*/
public static boolean isAscii(final 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('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 32 and 126 inclusive
*/
public static boolean isAsciiPrintable(final 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('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if less than 32 or equals 127
*/
public static boolean isAsciiControl(final 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('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 65 and 90 or 97 and 122 inclusive
*/
public static boolean isAsciiAlpha(final char ch) {
return isAsciiAlphaUpper(ch) || isAsciiAlphaLower(ch);
}
/**
* <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('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 65 and 90 inclusive
*/
public static boolean isAsciiAlphaUpper(final 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('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 97 and 122 inclusive
*/
public static boolean isAsciiAlphaLower(final 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('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 48 and 57 inclusive
*/
public static boolean isAsciiNumeric(final 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('&copy;') = 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(final char ch) {
return isAsciiAlpha(ch) || isAsciiNumeric(ch);
}
/**
* <p>Compares two {@code char} values numerically. This is the same functionality as provided in Java 7.</p>
*
* @param x the first {@code char} to compare
* @param y the second {@code char} to compare
* @return the value {@code 0} if {@code x == y};
* a value less than {@code 0} if {@code x < y}; and
* a value greater than {@code 0} if {@code x > y}
* @since 3.4
*/
public static int compare(final char x, final char y) {
return x-y;
}
}

View File

@ -1,230 +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 com.qihoo360.replugin.ext.lang3;
import com.qihoo360.replugin.ext.lang3.math.NumberUtils;
/**
* <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
*/
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"),
/**
* Java 1.9.
*
* @deprecated As of release 3.5, replaced by {@link #JAVA_9}
*/
@Deprecated
JAVA_1_9(9.0f, "9"),
/**
* Java 9
*/
JAVA_9(9.0f, "9"),
/**
* The most recent java version. Mainly introduced to avoid to break when a new version of Java is used.
*/
JAVA_RECENT(maxVersion(), Float.toString(maxVersion()));
/**
* The float value.
*/
private final float value;
/**
* The standard name.
*/
private final 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(final 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 if ("9".equals(nom)) {
return JAVA_9;
}
if (nom == null) {
return null;
}
final float v = toFloatVersion(nom);
if ((v - 1.) < 1.) { // then we need to check decimals > .9
final int firstComma = Math.max(nom.indexOf('.'), nom.indexOf(','));
final int end = Math.max(nom.length(), nom.indexOf(',', firstComma));
if (Float.parseFloat(nom.substring(firstComma + 1, end)) > .9f) {
return JAVA_RECENT;
}
}
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;
}
/**
* Gets the Java Version from the system or 99.0 if the {@code java.specification.version} system property is not set.
*
* @return the value of {@code java.specification.version} system property or 99.0 if it is not set.
*/
private static float maxVersion() {
final float v = toFloatVersion(System.getProperty("java.specification.version", "99.0"));
if (v > 0) {
return v;
}
return 99f;
}
/**
* Parses a float value from a String.
*
* @param value the String to parse.
* @return the float value represented by the string or -1 if the given String can not be parsed.
*/
private static float toFloatVersion(final String value) {
final int defaultReturnValue = -1;
if (value.contains(".")) {
final String[] toParse = value.split("\\.");
if (toParse.length >= 2) {
return NumberUtils.toFloat(toParse[0] + '.' + toParse[1], defaultReturnValue);
}
} else {
return NumberUtils.toFloat(value, defaultReturnValue);
}
return defaultReturnValue;
}
}

View File

@ -1,961 +0,0 @@
/*
* Copyright (c) 2016, Liu Dong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.qihoo360.replugin.ext.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 com.qihoo360.replugin.ext.lang3.exception.CloneFailedException;
import com.qihoo360.replugin.ext.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
*/
//@Immutable
@SuppressWarnings("deprecation") // deprecated class StrBuilder is imported
// because it is part of the signature of deprecated methods
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 HashMap#get(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(final T object, final 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
*/
@SafeVarargs
public static <T> T firstNonNull(final T... values) {
if (values != null) {
for (final T val : values) {
if (val != null) {
return val;
}
}
}
return null;
}
/**
* Checks if any value in the given array is not {@code null}.
*
* <p>
* If all the values are {@code null} or the array is {@code null}
* or empty then {@code false} is returned. Otherwise {@code true} is returned.
* </p>
*
* <pre>
* ObjectUtils.anyNotNull(*) = true
* ObjectUtils.anyNotNull(*, null) = true
* ObjectUtils.anyNotNull(null, *) = true
* ObjectUtils.anyNotNull(null, null, *, *) = true
* ObjectUtils.anyNotNull(null) = false
* ObjectUtils.anyNotNull(null, null) = false
* </pre>
*
* @param values the values to test, may be {@code null} or empty
* @return {@code true} if there is at least one non-null value in the array,
* {@code false} if all values in the array are {@code null}s.
* If the array is {@code null} or empty {@code false} is also returned.
* @since 3.5
*/
public static boolean anyNotNull(final Object... values) {
return firstNonNull(values) != null;
}
/**
* Checks if all values in the array are not {@code nulls}.
*
* <p>
* If any value is {@code null} or the array is {@code null} then
* {@code false} is returned. If all elements in array are not
* {@code null} or the array is empty (contains no elements) {@code true}
* is returned.
* </p>
*
* <pre>
* ObjectUtils.allNotNull(*) = true
* ObjectUtils.allNotNull(*, *) = true
* ObjectUtils.allNotNull(null) = false
* ObjectUtils.allNotNull(null, null) = false
* ObjectUtils.allNotNull(null, *) = false
* ObjectUtils.allNotNull(*, null) = false
* ObjectUtils.allNotNull(*, *, null, *) = false
* </pre>
*
* @param values the values to test, may be {@code null} or empty
* @return {@code false} if there is at least one {@code null} value in the array or the array is {@code null},
* {@code true} if all values in the array are not {@code null}s or array contains no elements.
* @since 3.5
*/
public static boolean allNotNull(final Object... values) {
if (values == null) {
return false;
}
for (final Object val : values) {
if (val == null) {
return false;
}
}
return true;
}
// 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
* @deprecated this method has been replaced by {@code java.util.Objects.equals(Object, Object)} in Java 7 and will
* be removed from future releases.
*/
@Deprecated
public static boolean equals(final Object object1, final 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(final Object object1, final 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
* @deprecated this method has been replaced by {@code java.util.Objects.hashCode(Object)} in Java 7 and will be
* removed in future releases
*/
@Deprecated
public static int hashCode(final Object obj) {
// hashCode(Object) retained for performance, as hash code is often critical
return obj == null ? 0 : obj.hashCode();
}
// 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(final Object object) {
if (object == null) {
return null;
}
final StringBuilder builder = new StringBuilder();
identityToString(builder, object);
return builder.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(final StringBuffer buffer, final Object object) {
Validate.notNull(object, "Cannot get the toString of a null identity");
buffer.append(object.getClass().getName())
.append('@')
.append(Integer.toHexString(System.identityHashCode(object)));
}
/**
* <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(builder, "") = builder.append("java.lang.String@1e23"
* ObjectUtils.identityToString(builder, Boolean.TRUE) = builder.append("java.lang.Boolean@7fa"
* ObjectUtils.identityToString(builder, Boolean.TRUE) = builder.append("java.lang.Boolean@7fa")
* </pre>
*
* @param builder the builder to append to
* @param object the object to create a toString for
* @since 3.2
*/
public static void identityToString(final StringBuilder builder, final Object object) {
Validate.notNull(object, "Cannot get the toString of a null identity");
builder.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 {@code ""} if {@code null} input
* @since 2.0
* @deprecated this method has been replaced by {@code java.util.Objects.toString(Object)} in Java 7 and will be
* removed in future releases. Note however that said method will return "null" for null references, while this
* method returns an empty String. To preserve behavior use {@code java.util.Objects.toString(myObject, "")}
*/
@Deprecated
public static String toString(final Object obj) {
return obj == null ? StringUtils.EMPTY : 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 {@code nullStr} if {@code null} input
* @since 2.0
* @deprecated this method has been replaced by {@code java.util.Objects.toString(Object, String)} in Java 7 and
* will be removed in future releases.
*/
@Deprecated
public static String toString(final Object obj, final 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>
*/
@SafeVarargs
public static <T extends Comparable<? super T>> T min(final T... values) {
T result = null;
if (values != null) {
for (final 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>
*/
@SafeVarargs
public static <T extends Comparable<? super T>> T max(final T... values) {
T result = null;
if (values != null) {
for (final 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 &lt; c2, zero if c1 = c2
* and a positive value if c1 &gt; c2
*/
public static <T extends Comparable<? super T>> int compare(final T c1, final 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 &lt; c2, zero if c1 = c2
* and a positive value if c1 &gt; c2
* @see Comparator#compare(Object, Object)
*/
public static <T extends Comparable<? super T>> int compare(final T c1, final T c2, final 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
*/
@SafeVarargs
public static <T extends Comparable<? super T>> T median(final T... items) {
Validate.notEmpty(items);
Validate.noNullElements(items);
final TreeSet<T> sort = new TreeSet<>();
Collections.addAll(sort, items);
@SuppressWarnings("unchecked") //we know all items added were T instances
final 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
*/
@SafeVarargs
public static <T> T median(final Comparator<T> comparator, final T... items) {
Validate.notEmpty(items, "null/empty items");
Validate.noNullElements(items);
Validate.notNull(comparator, "null comparator");
final TreeSet<T> sort = new TreeSet<>(comparator);
Collections.addAll(sort, items);
@SuppressWarnings("unchecked") //we know all items added were T instances
final
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
*/
@SafeVarargs
public static <T> T mode(final T... items) {
if (ArrayUtils.isNotEmpty(items)) {
final HashMap<T, MutableInt> occurrences = new HashMap<>(items.length);
for (final T t : items) {
final MutableInt count = occurrences.get(t);
if (count == null) {
occurrences.put(t, new MutableInt(1));
} else {
count.increment();
}
}
T result = null;
int max = 0;
for (final Map.Entry<T, MutableInt> e : occurrences.entrySet()) {
final 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") // OK because input is of type T
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 HashMap#get(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 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;
}
}
// Constants (LANG-816):
/*
These methods ensure constants are not inlined by javac.
For example, typically a developer might declare a constant like so:
public final static int MAGIC_NUMBER = 5;
Should a different jar file refer to this, and the MAGIC_NUMBER
is changed a later date (e.g., MAGIC_NUMBER = 6), the different jar
file will need to recompile itself. This is because javac
typically inlines the primitive or String constant directly into
the bytecode, and removes the reference to the MAGIC_NUMBER field.
To help the other jar (so that it does not need to recompile
when constants are changed) the original developer can declare
their constant using one of the CONST() utility methods, instead:
public final static int MAGIC_NUMBER = CONST(5);
*/
/**
* This method returns the provided value unchanged.
* This can prevent javac from inlining a constant
* field, e.g.,
*
* <pre>
* public final static boolean MAGIC_FLAG = ObjectUtils.CONST(true);
* </pre>
*
* This way any jars that refer to this field do not
* have to recompile themselves if the field's value
* changes at some future date.
*
* @param v the boolean value to return
* @return the boolean v, unchanged
* @since 3.2
*/
public static boolean CONST(final boolean v) {
return v;
}
/**
* This method returns the provided value unchanged.
* This can prevent javac from inlining a constant
* field, e.g.,
*
* <pre>
* public final static byte MAGIC_BYTE = ObjectUtils.CONST((byte) 127);
* </pre>
*
* This way any jars that refer to this field do not
* have to recompile themselves if the field's value
* changes at some future date.
*
* @param v the byte value to return
* @return the byte v, unchanged
* @since 3.2
*/
public static byte CONST(final byte v) {
return v;
}
/**
* This method returns the provided value unchanged.
* This can prevent javac from inlining a constant
* field, e.g.,
*
* <pre>
* public final static byte MAGIC_BYTE = ObjectUtils.CONST_BYTE(127);
* </pre>
*
* This way any jars that refer to this field do not
* have to recompile themselves if the field's value
* changes at some future date.
*
* @param v the byte literal (as an int) value to return
* @throws IllegalArgumentException if the value passed to v
* is larger than a byte, that is, smaller than -128 or
* larger than 127.
* @return the byte v, unchanged
* @since 3.2
*/
public static byte CONST_BYTE(final int v) throws IllegalArgumentException {
if (v < Byte.MIN_VALUE || v > Byte.MAX_VALUE) {
throw new IllegalArgumentException("Supplied value must be a valid byte literal between -128 and 127: [" + v + "]");
}
return (byte) v;
}
/**
* This method returns the provided value unchanged.
* This can prevent javac from inlining a constant
* field, e.g.,
*
* <pre>
* public final static char MAGIC_CHAR = ObjectUtils.CONST('a');
* </pre>
*
* This way any jars that refer to this field do not
* have to recompile themselves if the field's value
* changes at some future date.
*
* @param v the char value to return
* @return the char v, unchanged
* @since 3.2
*/
public static char CONST(final char v) {
return v;
}
/**
* This method returns the provided value unchanged.
* This can prevent javac from inlining a constant
* field, e.g.,
*
* <pre>
* public final static short MAGIC_SHORT = ObjectUtils.CONST((short) 123);
* </pre>
*
* This way any jars that refer to this field do not
* have to recompile themselves if the field's value
* changes at some future date.
*
* @param v the short value to return
* @return the short v, unchanged
* @since 3.2
*/
public static short CONST(final short v) {
return v;
}
/**
* This method returns the provided value unchanged.
* This can prevent javac from inlining a constant
* field, e.g.,
*
* <pre>
* public final static short MAGIC_SHORT = ObjectUtils.CONST_SHORT(127);
* </pre>
*
* This way any jars that refer to this field do not
* have to recompile themselves if the field's value
* changes at some future date.
*
* @param v the short literal (as an int) value to return
* @throws IllegalArgumentException if the value passed to v
* is larger than a short, that is, smaller than -32768 or
* larger than 32767.
* @return the byte v, unchanged
* @since 3.2
*/
public static short CONST_SHORT(final int v) throws IllegalArgumentException {
if (v < Short.MIN_VALUE || v > Short.MAX_VALUE) {
throw new IllegalArgumentException("Supplied value must be a valid byte literal between -32768 and 32767: [" + v + "]");
}
return (short) v;
}
/**
* This method returns the provided value unchanged.
* This can prevent javac from inlining a constant
* field, e.g.,
*
* <pre>
* public final static int MAGIC_INT = ObjectUtils.CONST(123);
* </pre>
*
* This way any jars that refer to this field do not
* have to recompile themselves if the field's value
* changes at some future date.
*
* @param v the int value to return
* @return the int v, unchanged
* @since 3.2
*/
public static int CONST(final int v) {
return v;
}
/**
* This method returns the provided value unchanged.
* This can prevent javac from inlining a constant
* field, e.g.,
*
* <pre>
* public final static long MAGIC_LONG = ObjectUtils.CONST(123L);
* </pre>
*
* This way any jars that refer to this field do not
* have to recompile themselves if the field's value
* changes at some future date.
*
* @param v the long value to return
* @return the long v, unchanged
* @since 3.2
*/
public static long CONST(final long v) {
return v;
}
/**
* This method returns the provided value unchanged.
* This can prevent javac from inlining a constant
* field, e.g.,
*
* <pre>
* public final static float MAGIC_FLOAT = ObjectUtils.CONST(1.0f);
* </pre>
*
* This way any jars that refer to this field do not
* have to recompile themselves if the field's value
* changes at some future date.
*
* @param v the float value to return
* @return the float v, unchanged
* @since 3.2
*/
public static float CONST(final float v) {
return v;
}
/**
* This method returns the provided value unchanged.
* This can prevent javac from inlining a constant
* field, e.g.,
*
* <pre>
* public final static double MAGIC_DOUBLE = ObjectUtils.CONST(1.0);
* </pre>
*
* This way any jars that refer to this field do not
* have to recompile themselves if the field's value
* changes at some future date.
*
* @param v the double value to return
* @return the double v, unchanged
* @since 3.2
*/
public static double CONST(final double v) {
return v;
}
/**
* This method returns the provided value unchanged.
* This can prevent javac from inlining a constant
* field, e.g.,
*
* <pre>
* public final static String MAGIC_STRING = ObjectUtils.CONST("abc");
* </pre>
*
* This way any jars that refer to this field do not
* have to recompile themselves if the field's value
* changes at some future date.
*
* @param <T> the Object type
* @param v the genericized Object value to return (typically a String).
* @return the genericized Object v, unchanged (typically a String).
* @since 3.2
*/
public static <T> T CONST(final T v) {
return v;
}
}

View File

@ -1,88 +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 com.qihoo360.replugin.ext.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:
* <pre><code>
* class FontBuilder implements Builder&lt;Font&gt; {
* 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;
* }
* }
* </code></pre>
*
* Example Builder Usage:
* <pre><code>
* Font bold14ptSansSerifFont = new FontBuilder(Font.SANS_SERIF).bold()
* .size(14.0f)
* .build();
* </code></pre>
*
*
* @param <T> the type of object that the builder will construct or compute.
*
* @since 3.0
*/
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.
*/
T build();
}

View File

@ -1,118 +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 com.qihoo360.replugin.ext.lang3.builder;
import com.qihoo360.replugin.ext.lang3.ObjectUtils;
import com.qihoo360.replugin.ext.lang3.reflect.TypeUtils;
import java.lang.reflect.Type;
import com.qihoo360.replugin.ext.lang3.tuple.Pair;
/**
* <p>
* A {@code Diff} contains the differences between two {@link Diffable} class
* fields.
* </p>
*
* <p>
* Typically, {@code Diff}s are retrieved by using a {@link DiffBuilder} to
* produce a {@link DiffResult}, containing the differences between two objects.
* </p>
*
*
* @param <T>
* The type of object contained within this {@code Diff}. Differences
* between primitive objects are stored as their Object wrapper
* equivalent.
* @since 3.3
*/
public abstract class Diff<T> extends Pair<T, T> {
private static final long serialVersionUID = 1L;
private final Type type;
private final String fieldName;
/**
* <p>
* Constructs a new {@code Diff} for the given field name.
* </p>
*
* @param fieldName
* the name of the field
*/
protected Diff(final String fieldName) {
this.type = ObjectUtils.defaultIfNull(
TypeUtils.getTypeArguments(getClass(), Diff.class).get(
Diff.class.getTypeParameters()[0]), Object.class);
this.fieldName = fieldName;
}
/**
* <p>
* Returns the type of the field.
* </p>
*
* @return the field type
*/
public final Type getType() {
return type;
}
/**
* <p>
* Returns the name of the field.
* </p>
*
* @return the field name
*/
public final String getFieldName() {
return fieldName;
}
/**
* <p>
* Returns a {@code String} representation of the {@code Diff}, with the
* following format:</p>
*
* <pre>
* [fieldname: left-value, right-value]
* </pre>
*
*
* @return the string representation
*/
@Override
public final String toString() {
return String.format("[%s: %s, %s]", fieldName, getLeft(), getRight());
}
/**
* <p>
* Throws {@code UnsupportedOperationException}.
* </p>
*
* @param value
* ignored
* @return nothing
*/
@Override
public final T setValue(final T value) {
throw new UnsupportedOperationException("Cannot alter Diff object.");
}
}

View File

@ -1,984 +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 com.qihoo360.replugin.ext.lang3.builder;
import com.qihoo360.replugin.ext.lang3.ArrayUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.qihoo360.replugin.ext.lang3.Validate;
/**
* <p>
* Assists in implementing {@link Diffable#diff(Object)} methods.
* </p>
*
* <p>
* To use this class, write code as follows:
* </p>
*
* <pre>
* public class Person implements Diffable&lt;Person&gt; {
* String name;
* int age;
* boolean smoker;
*
* ...
*
* public DiffResult diff(Person obj) {
* // No need for null check, as NullPointerException correct if obj is null
* return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
* .append("name", this.name, obj.name)
* .append("age", this.age, obj.age)
* .append("smoker", this.smoker, obj.smoker)
* .build();
* }
* }
* </pre>
*
* <p>
* The {@code ToStringStyle} passed to the constructor is embedded in the
* returned {@code DiffResult} and influences the style of the
* {@code DiffResult.toString()} method. This style choice can be overridden by
* calling {@link DiffResult#toString(ToStringStyle)}.
* </p>
*
* @since 3.3
* @see Diffable
* @see Diff
* @see DiffResult
* @see ToStringStyle
*/
public class DiffBuilder implements Builder<DiffResult> {
private final List<Diff<?>> diffs;
private final boolean objectsTriviallyEqual;
private final Object left;
private final Object right;
private final ToStringStyle style;
/**
* <p>
* Constructs a builder for the specified objects with the specified style.
* </p>
*
* <p>
* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
* not evaluate any calls to {@code append(...)} and will return an empty
* {@link DiffResult} when {@link #build()} is executed.
* </p>
*
* @param lhs
* {@code this} object
* @param rhs
* the object to diff against
* @param style
* the style will use when outputting the objects, {@code null}
* uses the default
* @param testTriviallyEqual
* If true, this will test if lhs and rhs are the same or equal.
* All of the append(fieldName, lhs, rhs) methods will abort
* without creating a field {@link Diff} if the trivially equal
* test is enabled and returns true. The result of this test
* is never changed throughout the life of this {@link DiffBuilder}.
* @throws IllegalArgumentException
* if {@code lhs} or {@code rhs} is {@code null}
* @since 3.4
*/
public DiffBuilder(final Object lhs, final Object rhs,
final ToStringStyle style, final boolean testTriviallyEqual) {
Validate.isTrue(lhs != null, "lhs cannot be null");
Validate.isTrue(rhs != null, "rhs cannot be null");
this.diffs = new ArrayList<>();
this.left = lhs;
this.right = rhs;
this.style = style;
// Don't compare any fields if objects equal
this.objectsTriviallyEqual = testTriviallyEqual && (lhs == rhs || lhs.equals(rhs));
}
/**
* <p>
* Constructs a builder for the specified objects with the specified style.
* </p>
*
* <p>
* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
* not evaluate any calls to {@code append(...)} and will return an empty
* {@link DiffResult} when {@link #build()} is executed.
* </p>
*
* <p>
* This delegates to {@link #DiffBuilder(Object, Object, ToStringStyle, boolean)}
* with the testTriviallyEqual flag enabled.
* </p>
*
* @param lhs
* {@code this} object
* @param rhs
* the object to diff against
* @param style
* the style will use when outputting the objects, {@code null}
* uses the default
* @throws IllegalArgumentException
* if {@code lhs} or {@code rhs} is {@code null}
*/
public DiffBuilder(final Object lhs, final Object rhs,
final ToStringStyle style) {
this(lhs, rhs, style, true);
}
/**
* <p>
* Test if two {@code boolean}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code boolean}
* @param rhs
* the right hand {@code boolean}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final boolean lhs,
final boolean rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (lhs != rhs) {
diffs.add(new Diff<Boolean>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Boolean getLeft() {
return Boolean.valueOf(lhs);
}
@Override
public Boolean getRight() {
return Boolean.valueOf(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code boolean[]}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code boolean[]}
* @param rhs
* the right hand {@code boolean[]}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final boolean[] lhs,
final boolean[] rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (!Arrays.equals(lhs, rhs)) {
diffs.add(new Diff<Boolean[]>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Boolean[] getLeft() {
return ArrayUtils.toObject(lhs);
}
@Override
public Boolean[] getRight() {
return ArrayUtils.toObject(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code byte}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code byte}
* @param rhs
* the right hand {@code byte}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final byte lhs,
final byte rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (lhs != rhs) {
diffs.add(new Diff<Byte>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Byte getLeft() {
return Byte.valueOf(lhs);
}
@Override
public Byte getRight() {
return Byte.valueOf(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code byte[]}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code byte[]}
* @param rhs
* the right hand {@code byte[]}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final byte[] lhs,
final byte[] rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (!Arrays.equals(lhs, rhs)) {
diffs.add(new Diff<Byte[]>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Byte[] getLeft() {
return ArrayUtils.toObject(lhs);
}
@Override
public Byte[] getRight() {
return ArrayUtils.toObject(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code char}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code char}
* @param rhs
* the right hand {@code char}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final char lhs,
final char rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (lhs != rhs) {
diffs.add(new Diff<Character>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Character getLeft() {
return Character.valueOf(lhs);
}
@Override
public Character getRight() {
return Character.valueOf(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code char[]}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code char[]}
* @param rhs
* the right hand {@code char[]}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final char[] lhs,
final char[] rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (!Arrays.equals(lhs, rhs)) {
diffs.add(new Diff<Character[]>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Character[] getLeft() {
return ArrayUtils.toObject(lhs);
}
@Override
public Character[] getRight() {
return ArrayUtils.toObject(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code double}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code double}
* @param rhs
* the right hand {@code double}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final double lhs,
final double rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (Double.doubleToLongBits(lhs) != Double.doubleToLongBits(rhs)) {
diffs.add(new Diff<Double>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Double getLeft() {
return Double.valueOf(lhs);
}
@Override
public Double getRight() {
return Double.valueOf(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code double[]}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code double[]}
* @param rhs
* the right hand {@code double[]}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final double[] lhs,
final double[] rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (!Arrays.equals(lhs, rhs)) {
diffs.add(new Diff<Double[]>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Double[] getLeft() {
return ArrayUtils.toObject(lhs);
}
@Override
public Double[] getRight() {
return ArrayUtils.toObject(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code float}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code float}
* @param rhs
* the right hand {@code float}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final float lhs,
final float rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (Float.floatToIntBits(lhs) != Float.floatToIntBits(rhs)) {
diffs.add(new Diff<Float>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Float getLeft() {
return Float.valueOf(lhs);
}
@Override
public Float getRight() {
return Float.valueOf(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code float[]}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code float[]}
* @param rhs
* the right hand {@code float[]}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final float[] lhs,
final float[] rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (!Arrays.equals(lhs, rhs)) {
diffs.add(new Diff<Float[]>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Float[] getLeft() {
return ArrayUtils.toObject(lhs);
}
@Override
public Float[] getRight() {
return ArrayUtils.toObject(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code int}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code int}
* @param rhs
* the right hand {@code int}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final int lhs,
final int rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (lhs != rhs) {
diffs.add(new Diff<Integer>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Integer getLeft() {
return Integer.valueOf(lhs);
}
@Override
public Integer getRight() {
return Integer.valueOf(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code int[]}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code int[]}
* @param rhs
* the right hand {@code int[]}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final int[] lhs,
final int[] rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (!Arrays.equals(lhs, rhs)) {
diffs.add(new Diff<Integer[]>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Integer[] getLeft() {
return ArrayUtils.toObject(lhs);
}
@Override
public Integer[] getRight() {
return ArrayUtils.toObject(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code long}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code long}
* @param rhs
* the right hand {@code long}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final long lhs,
final long rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (lhs != rhs) {
diffs.add(new Diff<Long>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Long getLeft() {
return Long.valueOf(lhs);
}
@Override
public Long getRight() {
return Long.valueOf(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code long[]}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code long[]}
* @param rhs
* the right hand {@code long[]}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final long[] lhs,
final long[] rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (!Arrays.equals(lhs, rhs)) {
diffs.add(new Diff<Long[]>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Long[] getLeft() {
return ArrayUtils.toObject(lhs);
}
@Override
public Long[] getRight() {
return ArrayUtils.toObject(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code short}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code short}
* @param rhs
* the right hand {@code short}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final short lhs,
final short rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (lhs != rhs) {
diffs.add(new Diff<Short>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Short getLeft() {
return Short.valueOf(lhs);
}
@Override
public Short getRight() {
return Short.valueOf(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code short[]}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code short[]}
* @param rhs
* the right hand {@code short[]}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final short[] lhs,
final short[] rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (!Arrays.equals(lhs, rhs)) {
diffs.add(new Diff<Short[]>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Short[] getLeft() {
return ArrayUtils.toObject(lhs);
}
@Override
public Short[] getRight() {
return ArrayUtils.toObject(rhs);
}
});
}
return this;
}
/**
* <p>
* Test if two {@code Objects}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code Object}
* @param rhs
* the right hand {@code Object}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final Object lhs,
final Object rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (lhs == rhs) {
return this;
}
Object objectToTest;
if (lhs != null) {
objectToTest = lhs;
} else {
// rhs cannot be null, as lhs != rhs
objectToTest = rhs;
}
if (objectToTest.getClass().isArray()) {
if (objectToTest instanceof boolean[]) {
return append(fieldName, (boolean[]) lhs, (boolean[]) rhs);
}
if (objectToTest instanceof byte[]) {
return append(fieldName, (byte[]) lhs, (byte[]) rhs);
}
if (objectToTest instanceof char[]) {
return append(fieldName, (char[]) lhs, (char[]) rhs);
}
if (objectToTest instanceof double[]) {
return append(fieldName, (double[]) lhs, (double[]) rhs);
}
if (objectToTest instanceof float[]) {
return append(fieldName, (float[]) lhs, (float[]) rhs);
}
if (objectToTest instanceof int[]) {
return append(fieldName, (int[]) lhs, (int[]) rhs);
}
if (objectToTest instanceof long[]) {
return append(fieldName, (long[]) lhs, (long[]) rhs);
}
if (objectToTest instanceof short[]) {
return append(fieldName, (short[]) lhs, (short[]) rhs);
}
return append(fieldName, (Object[]) lhs, (Object[]) rhs);
}
// Not array type
if (lhs != null && lhs.equals(rhs)) {
return this;
}
diffs.add(new Diff<Object>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Object getLeft() {
return lhs;
}
@Override
public Object getRight() {
return rhs;
}
});
return this;
}
/**
* <p>
* Test if two {@code Object[]}s are equal.
* </p>
*
* @param fieldName
* the field name
* @param lhs
* the left hand {@code Object[]}
* @param rhs
* the right hand {@code Object[]}
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
*/
public DiffBuilder append(final String fieldName, final Object[] lhs,
final Object[] rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (!Arrays.equals(lhs, rhs)) {
diffs.add(new Diff<Object[]>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Object[] getLeft() {
return lhs;
}
@Override
public Object[] getRight() {
return rhs;
}
});
}
return this;
}
/**
* <p>
* Append diffs from another {@code DiffResult}.
* </p>
*
* <p>
* This method is useful if you want to compare properties which are
* themselves Diffable and would like to know which specific part of
* it is different.
* </p>
*
* <pre>
* public class Person implements Diffable&lt;Person&gt; {
* String name;
* Address address; // implements Diffable&lt;Address&gt;
*
* ...
*
* public DiffResult diff(Person obj) {
* return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
* .append("name", this.name, obj.name)
* .append("address", this.address.diff(obj.address))
* .build();
* }
* }
* </pre>
*
* @param fieldName
* the field name
* @param diffResult
* the {@code DiffResult} to append
* @return this
* @throws IllegalArgumentException
* if field name is {@code null}
* @since 3.5
*/
public DiffBuilder append(final String fieldName,
final DiffResult diffResult) {
validateFieldNameNotNull(fieldName);
Validate.isTrue(diffResult != null, "Diff result cannot be null");
if (objectsTriviallyEqual) {
return this;
}
for (final Diff<?> diff : diffResult.getDiffs()) {
append(fieldName + "." + diff.getFieldName(),
diff.getLeft(), diff.getRight());
}
return this;
}
/**
* <p>
* Builds a {@link DiffResult} based on the differences appended to this
* builder.
* </p>
*
* @return a {@code DiffResult} containing the differences between the two
* objects.
*/
@Override
public DiffResult build() {
return new DiffResult(left, right, diffs, style);
}
private void validateFieldNameNotNull(final String fieldName) {
Validate.isTrue(fieldName != null, "Field name cannot be null");
}
}

View File

@ -1,200 +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 com.qihoo360.replugin.ext.lang3.builder;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import com.qihoo360.replugin.ext.lang3.Validate;
/**
* <p>
* A {@code DiffResult} contains a collection of the differences between two
* {@link Diffable} objects. Typically these differences are displayed using
* {@link #toString()} method, which returns a string describing the fields that
* differ between the objects.
* </p>
* <p>
* Use a {@link DiffBuilder} to build a {@code DiffResult} comparing two objects.
* </p>
*
* @since 3.3
*/
public class DiffResult implements Iterable<Diff<?>> {
/**
* <p>
* The {@code String} returned when the objects have no differences:
* {@value}
* </p>
*/
public static final String OBJECTS_SAME_STRING = "";
private static final String DIFFERS_STRING = "differs from";
private final List<Diff<?>> diffs;
private final Object lhs;
private final Object rhs;
private final ToStringStyle style;
/**
* <p>
* Creates a {@link DiffResult} containing the differences between two
* objects.
* </p>
*
* @param lhs
* the left hand object
* @param rhs
* the right hand object
* @param diffs
* the list of differences, may be empty
* @param style
* the style to use for the {@link #toString()} method. May be
* {@code null}, in which case
* {@link ToStringStyle#DEFAULT_STYLE} is used
* @throws IllegalArgumentException
* if {@code lhs}, {@code rhs} or {@code diffs} is {@code null}
*/
DiffResult(final Object lhs, final Object rhs, final List<Diff<?>> diffs,
final ToStringStyle style) {
Validate.isTrue(lhs != null, "Left hand object cannot be null");
Validate.isTrue(rhs != null, "Right hand object cannot be null");
Validate.isTrue(diffs != null, "List of differences cannot be null");
this.diffs = diffs;
this.lhs = lhs;
this.rhs = rhs;
if (style == null) {
this.style = ToStringStyle.DEFAULT_STYLE;
} else {
this.style = style;
}
}
/**
* <p>
* Returns an unmodifiable list of {@code Diff}s. The list may be empty if
* there were no differences between the objects.
* </p>
*
* @return an unmodifiable list of {@code Diff}s
*/
public List<Diff<?>> getDiffs() {
return Collections.unmodifiableList(diffs);
}
/**
* <p>
* Returns the number of differences between the two objects.
* </p>
*
* @return the number of differences
*/
public int getNumberOfDiffs() {
return diffs.size();
}
/**
* <p>
* Returns the style used by the {@link #toString()} method.
* </p>
*
* @return the style
*/
public ToStringStyle getToStringStyle() {
return style;
}
/**
* <p>
* Builds a {@code String} description of the differences contained within
* this {@code DiffResult}. A {@link ToStringBuilder} is used for each object
* and the style of the output is governed by the {@code ToStringStyle}
* passed to the constructor.
* </p>
*
* <p>
* If there are no differences stored in this list, the method will return
* {@link #OBJECTS_SAME_STRING}. Otherwise, using the example given in
* {@link Diffable} and {@link ToStringStyle#SHORT_PREFIX_STYLE}, an output
* might be:
* </p>
*
* <pre>
* Person[name=John Doe,age=32] differs from Person[name=Joe Bloggs,age=26]
* </pre>
*
* <p>
* This indicates that the objects differ in name and age, but not in
* smoking status.
* </p>
*
* <p>
* To use a different {@code ToStringStyle} for an instance of this class,
* use {@link #toString(ToStringStyle)}.
* </p>
*
* @return a {@code String} description of the differences.
*/
@Override
public String toString() {
return toString(style);
}
/**
* <p>
* Builds a {@code String} description of the differences contained within
* this {@code DiffResult}, using the supplied {@code ToStringStyle}.
* </p>
*
* @param style
* the {@code ToStringStyle} to use when outputting the objects
*
* @return a {@code String} description of the differences.
*/
public String toString(final ToStringStyle style) {
if (diffs.size() == 0) {
return OBJECTS_SAME_STRING;
}
final ToStringBuilder lhsBuilder = new ToStringBuilder(lhs, style);
final ToStringBuilder rhsBuilder = new ToStringBuilder(rhs, style);
for (final Diff<?> diff : diffs) {
lhsBuilder.append(diff.getFieldName(), diff.getLeft());
rhsBuilder.append(diff.getFieldName(), diff.getRight());
}
return String.format("%s %s %s", lhsBuilder.build(), DIFFERS_STRING,
rhsBuilder.build());
}
/**
* <p>
* Returns an iterator over the {@code Diff} objects contained in this list.
* </p>
*
* @return the iterator
*/
@Override
public Iterator<Diff<?>> iterator() {
return diffs.iterator();
}
}

View File

@ -1,53 +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 com.qihoo360.replugin.ext.lang3.builder;
/**
* <p>{@code Diffable} classes can be compared with other objects
* for differences. The {@link DiffResult} object retrieved can be queried
* for a list of differences or printed using the {@link DiffResult#toString()}.</p>
*
* <p>The calculation of the differences is <i>consistent with equals</i> if
* and only if {@code d1.equals(d2)} implies {@code d1.diff(d2) == ""}.
* It is strongly recommended that implementations are consistent with equals
* to avoid confusion. Note that {@code null} is not an instance of any class
* and {@code d1.diff(null)} should throw a {@code NullPointerException}.</p>
*
* <p>
* {@code Diffable} classes lend themselves well to unit testing, in which a
* easily readable description of the differences between an anticipated result and
* an actual result can be retrieved. For example:
* </p>
* <pre>
* Assert.assertEquals(expected.diff(result), expected, result);
* </pre>
*
* @param <T> the type of objects that this object may be differentiated against
* @since 3.3
*/
public interface Diffable<T> {
/**
* <p>Retrieves a list of the differences between
* this object and the supplied object.</p>
*
* @param obj the object to diff against, can be {@code null}
* @return a list of differences
* @throws NullPointerException if the specified object is {@code null}
*/
DiffResult diff(T obj);
}

View File

@ -1,36 +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 com.qihoo360.replugin.ext.lang3.builder;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Use this annotation to exclude a field from being being used by
* the various <code>reflectionEquals</code> methods defined on
* {@link EqualsBuilder}.
*
* @since 3.5
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface EqualsExclude {
}

View File

@ -1,993 +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 com.qihoo360.replugin.ext.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 com.qihoo360.replugin.ext.lang3.ArrayUtils;
import com.qihoo360.replugin.ext.lang3.Validate;
/**
* <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://www.oracle.com/technetwork/java/effectivejava-136174.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 hash code 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>
*
* <p>The {@link HashCodeExclude} annotation can be used to exclude fields from being
* used by the <code>reflectionHashCode</code> methods.</p>
*
* @since 1.0
*/
public class HashCodeBuilder implements Builder<Integer> {
/**
* The default initial value to use in reflection hash code building.
*/
private static final int DEFAULT_INITIAL_VALUE = 17;
/**
* The default multiplier value to use in reflection hash code building.
*/
private static final int DEFAULT_MULTIPLIER_VALUE = 37;
/**
* <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<>();
/*
* 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 hash code 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.identityHashCode()
* 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(final Object value) {
final 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(final Object object, final Class<?> clazz, final HashCodeBuilder builder, final boolean useTransients,
final String[] excludeFields) {
if (isRegistered(object)) {
return;
}
try {
register(object);
final Field[] fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
for (final Field field : fields) {
if (!ArrayUtils.contains(excludeFields, field.getName())
&& !field.getName().contains("$")
&& (useTransients || !Modifier.isTransient(field.getModifiers()))
&& !Modifier.isStatic(field.getModifiers())
&& !field.isAnnotationPresent(HashCodeExclude.class)) {
try {
final Object fieldValue = field.get(object);
builder.append(fieldValue);
} catch (final 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>
* Uses reflection to build a valid hash code from the fields of {@code object}.
* </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. This will be the returned
* value if no fields are found to include in the hash code
* @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
*
* @see HashCodeExclude
*/
public static int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final Object object) {
return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null);
}
/**
* <p>
* Uses reflection to build a valid hash code from the fields of {@code object}.
* </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. This will be the returned
* value if no fields are found to include in the hash code
* @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
*
* @see HashCodeExclude
*/
public static int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final Object object,
final boolean testTransients) {
return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null);
}
/**
* <p>
* Uses reflection to build a valid hash code from the fields of {@code object}.
* </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. This will be the returned
* value if no fields are found to include in the hash code
* @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
*
* @see HashCodeExclude
* @since 2.0
*/
public static <T> int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final T object,
final boolean testTransients, final Class<? super T> reflectUpToClass, final String... excludeFields) {
Validate.isTrue(object != null, "The object to build a hash code for must not be null");
final 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>
* Uses reflection to build a valid hash code from the fields of {@code object}.
* </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. If no fields are found to include
* in the hash code, the result of this method will be constant.
* </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>
*
* @see HashCodeExclude
*/
public static int reflectionHashCode(final Object object, final boolean testTransients) {
return reflectionHashCode(DEFAULT_INITIAL_VALUE, DEFAULT_MULTIPLIER_VALUE, object,
testTransients, null);
}
/**
* <p>
* Uses reflection to build a valid hash code from the fields of {@code object}.
* </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. If no fields are found to include
* in the hash code, the result of this method will be constant.
* </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>
*
* @see HashCodeExclude
*/
public static int reflectionHashCode(final Object object, final Collection<String> excludeFields) {
return reflectionHashCode(object, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
}
// -------------------------------------------------------------------------
/**
* <p>
* Uses reflection to build a valid hash code from the fields of {@code object}.
* </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. If no fields are found to include
* in the hash code, the result of this method will be constant.
* </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>
*
* @see HashCodeExclude
*/
public static int reflectionHashCode(final Object object, final String... excludeFields) {
return reflectionHashCode(DEFAULT_INITIAL_VALUE, DEFAULT_MULTIPLIER_VALUE, 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.
*/
private static void register(final Object value) {
Set<IDKey> registry = getRegistry();
if (registry == null) {
registry = new HashSet<>();
REGISTRY.set(registry);
}
registry.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
*/
private static void unregister(final Object value) {
final Set<IDKey> registry = getRegistry();
if (registry != null) {
registry.remove(new IDKey(value));
if (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, 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 initialOddNumber
* an odd number used as the initial value
* @param multiplierOddNumber
* an odd number used as the multiplier
* @throws IllegalArgumentException
* if the number is even
*/
public HashCodeBuilder(final int initialOddNumber, final int multiplierOddNumber) {
Validate.isTrue(initialOddNumber % 2 != 0, "HashCodeBuilder requires an odd initial value");
Validate.isTrue(multiplierOddNumber % 2 != 0, "HashCodeBuilder requires an odd multiplier");
iConstant = multiplierOddNumber;
iTotal = initialOddNumber;
}
/**
* <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 <i>Effective Java</i> design.
* </p>
*
* @param value
* the boolean to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(final 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(final boolean[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (final 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(final 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(final byte[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (final 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(final 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(final char[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (final 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(final 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(final double[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (final 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(final 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(final float[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (final 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(final 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(final int[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (final 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(final 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(final long[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (final 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(final Object object) {
if (object == null) {
iTotal = iTotal * iConstant;
} else {
if (object.getClass().isArray()) {
// factor out array case in order to keep method small enough
// to be inlined
appendArray(object);
} else {
iTotal = iTotal * iConstant + object.hashCode();
}
}
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for an array.
* </p>
*
* @param object
* the array to add to the <code>hashCode</code>
*/
private void appendArray(final Object object) {
// '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);
}
}
/**
* <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(final Object[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (final 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(final 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(final short[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (final 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(final 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
*/
@Override
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();
}
}

View File

@ -1,36 +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 com.qihoo360.replugin.ext.lang3.builder;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Use this annotation to exclude a field from being being used by
* the various <code>reflectionHashcode</code> methods defined on
* {@link HashCodeBuilder}.
*
* @since 3.5
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface HashCodeExclude {
}

View File

@ -1,72 +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 com.qihoo360.replugin.ext.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
*/
IDKey(final Object _value) {
// This is the Object hash code
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 hash code - 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(final Object other) {
if (!(other instanceof IDKey)) {
return false;
}
final IDKey idKey = (IDKey) other;
if (id != idKey.id) {
return false;
}
// Note that identity equals is used.
return value == idKey.value;
}
}

View File

@ -1,99 +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 com.qihoo360.replugin.ext.lang3.builder;
import java.util.Collection;
import com.qihoo360.replugin.ext.lang3.ClassUtils;
/**
* <p>Works with {@link ToStringBuilder} to create a "deep" <code>toString</code>.</p>
*
* <p>To use this class write code as follows:</p>
*
* <pre>
* public class Job {
* String title;
* ...
* }
*
* public class Person {
* String name;
* int age;
* boolean smoker;
* Job job;
*
* ...
*
* public String toString() {
* return new ReflectionToStringBuilder(this, new RecursiveToStringStyle()).toString();
* }
* }
* </pre>
*
* <p>This will produce a toString of the format:
* <code>Person@7f54[name=Stephen,age=29,smoker=false,job=Job@43cd2[title=Manager]]</code></p>
*
* @since 3.2
*/
public class RecursiveToStringStyle extends ToStringStyle {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 1L;
/**
* <p>Constructor.</p>
*/
public RecursiveToStringStyle() {
super();
}
@Override
public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
if (!ClassUtils.isPrimitiveWrapper(value.getClass()) &&
!String.class.equals(value.getClass()) &&
accept(value.getClass())) {
buffer.append(ReflectionToStringBuilder.toString(value, this));
} else {
super.appendDetail(buffer, fieldName, value);
}
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
appendClassName(buffer, coll);
appendIdentityHashCode(buffer, coll);
appendDetail(buffer, fieldName, coll.toArray());
}
/**
* Returns whether or not to recursively format the given <code>Class</code>.
* By default, this method always returns {@code true}, but may be overwritten by
* sub-classes to filter specific classes.
*
* @param clazz
* The class to test.
* @return Whether or not to recursively format the given <code>Class</code>.
*/
protected boolean accept(final Class<?> clazz) {
return true;
}
}

View File

@ -1,846 +0,0 @@
/*
* Copyright (c) 2016, Liu Dong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.qihoo360.replugin.ext.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 com.qihoo360.replugin.ext.lang3.ArrayUtils;
import com.qihoo360.replugin.ext.lang3.ClassUtils;
import com.qihoo360.replugin.ext.lang3.Validate;
/**
* <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 AccessibleObject#setAccessible(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(&quot;An object: &quot; + ReflectionToStringBuilder.toString(anObject));
* </pre>
* <p>
* A subclass can control field output by overriding the methods:
* </p>
* <ul>
* <li>{@link #accept(Field)}</li>
* <li>{@link #getValue(Field)}</li>
* </ul>
* <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) &amp;&amp; !f.getName().equals(&quot;password&quot;);
* }
* }).toString();
* }
* </pre>
* <p>
* Alternatively the {@link ToStringExclude} annotation can be used to exclude fields from being incorporated in the
* result.
* </p>
* <p>
* The exact format of the <code>toString</code> is determined by the {@link ToStringStyle} passed into the constructor.
* </p>
*
* <p>
* <b>Note:</b> the default {@link ToStringStyle} will only do a "shallow" formatting, i.e. composed objects are not
* further traversed. To get "deep" formatting, use an instance of {@link RecursiveToStringStyle}.
* </p>
*
* @since 2.0
*/
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>
*
* @see ToStringExclude
*/
public static String toString(final 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>
*
* @see ToStringExclude
*/
public static String toString(final Object object, final 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>
*
* @see ToStringExclude
*/
public static String toString(final Object object, final ToStringStyle style, final 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 static fields
* @return the String result
* @throws IllegalArgumentException
* if the Object is <code>null</code>
*
* @see ToStringExclude
* @since 2.1
*/
public static String toString(final Object object, final ToStringStyle style, final boolean outputTransients, final 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>
*
* @see ToStringExclude
* @since 2.1
*/
public static <T> String toString(
final T object, final ToStringStyle style, final boolean outputTransients,
final boolean outputStatics, final Class<? super T> reflectUpToClass) {
return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics)
.toString();
}
/**
* <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 excludeNullValues
* whether to exclude fields whose values are null
* @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>
*
* @see ToStringExclude
* @since 3.6
*/
public static <T> String toString(
final T object, final ToStringStyle style, final boolean outputTransients,
final boolean outputStatics, boolean excludeNullValues, final Class<? super T> reflectUpToClass) {
return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics, excludeNullValues)
.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(final Object object, final 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(final 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(final Object[] array) {
final List<String> list = new ArrayList<>(array.length);
for (final 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(final Object object, final String... excludeFieldNames) {
return new ReflectionToStringBuilder(object).setExcludeFieldNames(excludeFieldNames).toString();
}
private static Object checkNotNull(final Object obj) {
Validate.isTrue(obj != null, "The Object passed in should not be null.");
return obj;
}
/**
* Whether or not to append static fields.
*/
private boolean appendStatics = false;
/**
* Whether or not to append transient fields.
*/
private boolean appendTransients = false;
/**
* Whether or not to append fields that are null.
*/
private boolean excludeNullValues;
/**
* 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(final Object object) {
super(checkNotNull(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(final Object object, final ToStringStyle style) {
super(checkNotNull(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(final Object object, final ToStringStyle style, final StringBuffer buffer) {
super(checkNotNull(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(
final T object, final ToStringStyle style, final StringBuffer buffer,
final Class<? super T> reflectUpToClass, final boolean outputTransients, final boolean outputStatics) {
super(checkNotNull(object), style, buffer);
this.setUpToClass(reflectUpToClass);
this.setAppendTransients(outputTransients);
this.setAppendStatics(outputStatics);
}
/**
* 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
* @param excludeNullValues
* whether to exclude fields who value is null
* @since 3.6
*/
public <T> ReflectionToStringBuilder(
final T object, final ToStringStyle style, final StringBuffer buffer,
final Class<? super T> reflectUpToClass, final boolean outputTransients, final boolean outputStatics,
final boolean excludeNullValues) {
super(checkNotNull(object), style, buffer);
this.setUpToClass(reflectUpToClass);
this.setAppendTransients(outputTransients);
this.setAppendStatics(outputStatics);
this.setExcludeNullValues(excludeNullValues);
}
/**
* 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 appended.</li>
* </ul>
*
* @param field
* The Field to test.
* @return Whether or not to append the given <code>Field</code>.
*/
protected boolean accept(final 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 !field.isAnnotationPresent(ToStringExclude.class);
}
/**
* <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 &quot;toString()'ed&quot;, 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(final Class<?> clazz) {
if (clazz.isArray()) {
this.reflectionAppendArray(this.getObject());
return;
}
final Field[] fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
for (final Field field : fields) {
final String fieldName = field.getName();
if (this.accept(field)) {
try {
// Warning: Field.get(Object) creates wrappers objects
// for primitive types.
final Object fieldValue = this.getValue(field);
if (!excludeNullValues || fieldValue != null) {
this.append(fieldName, fieldValue);
}
} catch (final 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 Field#get(Object)}
* @throws IllegalAccessException
* see {@link Field#get(Object)}
*
* @see Field#get(Object)
*/
protected Object getValue(final 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>
* Gets whether or not to append fields whose values are null.
* </p>
*
* @return Whether or not to append fields whose values are null.
* @since 3.6
*/
public boolean isExcludeNullValues() {
return this.excludeNullValues;
}
/**
* <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(final 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(final 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(final boolean appendTransients) {
this.appendTransients = appendTransients;
}
/**
* <p>
* Sets whether or not to append fields whose values are null.
* </p>
*
* @param excludeNullValues
* Whether or not to append fields whose values are null.
* @since 3.6
*/
public void setExcludeNullValues(final boolean excludeNullValues) {
this.excludeNullValues = excludeNullValues;
}
/**
* 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(final 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(final Class<?> clazz) {
if (clazz != null) {
final 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();
}
}

View File

@ -1,559 +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 com.qihoo360.replugin.ext.lang3.builder;
/**
* <p>Works with {@link ToStringBuilder} to create a <code>toString</code>.</p>
*
* <p>This class is intended to be used as a singleton.
* There is no need to instantiate a new style each time.
* Simply instantiate the class once, customize the values as required, and
* store the result in a public static final variable for the rest of the
* program to access.</p>
*
* @since 1.0
*/
public class StandardToStringStyle extends ToStringStyle {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 1L;
/**
* <p>Constructor.</p>
*/
public StandardToStringStyle() {
super();
}
//---------------------------------------------------------------------
/**
* <p>Gets whether to use the class name.</p>
*
* @return the current useClassName flag
*/
@Override
public boolean isUseClassName() { // NOPMD as this is implementing the abstract class
return super.isUseClassName();
}
/**
* <p>Sets whether to use the class name.</p>
*
* @param useClassName the new useClassName flag
*/
@Override
public void setUseClassName(final boolean useClassName) { // NOPMD as this is implementing the abstract class
super.setUseClassName(useClassName);
}
//---------------------------------------------------------------------
/**
* <p>Gets whether to output short or long class names.</p>
*
* @return the current useShortClassName flag
* @since 2.0
*/
@Override
public boolean isUseShortClassName() { // NOPMD as this is implementing the abstract class
return super.isUseShortClassName();
}
/**
* <p>Sets whether to output short or long class names.</p>
*
* @param useShortClassName the new useShortClassName flag
* @since 2.0
*/
@Override
public void setUseShortClassName(final boolean useShortClassName) { // NOPMD as this is implementing the abstract class
super.setUseShortClassName(useShortClassName);
}
//---------------------------------------------------------------------
/**
* <p>Gets whether to use the identity hash code.</p>
* @return the current useIdentityHashCode flag
*/
@Override
public boolean isUseIdentityHashCode() { // NOPMD as this is implementing the abstract class
return super.isUseIdentityHashCode();
}
/**
* <p>Sets whether to use the identity hash code.</p>
*
* @param useIdentityHashCode the new useIdentityHashCode flag
*/
@Override
public void setUseIdentityHashCode(final boolean useIdentityHashCode) { // NOPMD as this is implementing the abstract class
super.setUseIdentityHashCode(useIdentityHashCode);
}
//---------------------------------------------------------------------
/**
* <p>Gets whether to use the field names passed in.</p>
*
* @return the current useFieldNames flag
*/
@Override
public boolean isUseFieldNames() { // NOPMD as this is implementing the abstract class
return super.isUseFieldNames();
}
/**
* <p>Sets whether to use the field names passed in.</p>
*
* @param useFieldNames the new useFieldNames flag
*/
@Override
public void setUseFieldNames(final boolean useFieldNames) { // NOPMD as this is implementing the abstract class
super.setUseFieldNames(useFieldNames);
}
//---------------------------------------------------------------------
/**
* <p>Gets whether to use full detail when the caller doesn't
* specify.</p>
*
* @return the current defaultFullDetail flag
*/
@Override
public boolean isDefaultFullDetail() { // NOPMD as this is implementing the abstract class
return super.isDefaultFullDetail();
}
/**
* <p>Sets whether to use full detail when the caller doesn't
* specify.</p>
*
* @param defaultFullDetail the new defaultFullDetail flag
*/
@Override
public void setDefaultFullDetail(final boolean defaultFullDetail) { // NOPMD as this is implementing the abstract class
super.setDefaultFullDetail(defaultFullDetail);
}
//---------------------------------------------------------------------
/**
* <p>Gets whether to output array content detail.</p>
*
* @return the current array content detail setting
*/
@Override
public boolean isArrayContentDetail() { // NOPMD as this is implementing the abstract class
return super.isArrayContentDetail();
}
/**
* <p>Sets whether to output array content detail.</p>
*
* @param arrayContentDetail the new arrayContentDetail flag
*/
@Override
public void setArrayContentDetail(final boolean arrayContentDetail) { // NOPMD as this is implementing the abstract class
super.setArrayContentDetail(arrayContentDetail);
}
//---------------------------------------------------------------------
/**
* <p>Gets the array start text.</p>
*
* @return the current array start text
*/
@Override
public String getArrayStart() { // NOPMD as this is implementing the abstract class
return super.getArrayStart();
}
/**
* <p>Sets the array start text.</p>
*
* <p><code>null</code> is accepted, but will be converted
* to an empty String.</p>
*
* @param arrayStart the new array start text
*/
@Override
public void setArrayStart(final String arrayStart) { // NOPMD as this is implementing the abstract class
super.setArrayStart(arrayStart);
}
//---------------------------------------------------------------------
/**
* <p>Gets the array end text.</p>
*
* @return the current array end text
*/
@Override
public String getArrayEnd() { // NOPMD as this is implementing the abstract class
return super.getArrayEnd();
}
/**
* <p>Sets the array end text.</p>
*
* <p><code>null</code> is accepted, but will be converted
* to an empty String.</p>
*
* @param arrayEnd the new array end text
*/
@Override
public void setArrayEnd(final String arrayEnd) { // NOPMD as this is implementing the abstract class
super.setArrayEnd(arrayEnd);
}
//---------------------------------------------------------------------
/**
* <p>Gets the array separator text.</p>
*
* @return the current array separator text
*/
@Override
public String getArraySeparator() { // NOPMD as this is implementing the abstract class
return super.getArraySeparator();
}
/**
* <p>Sets the array separator text.</p>
*
* <p><code>null</code> is accepted, but will be converted
* to an empty String.</p>
*
* @param arraySeparator the new array separator text
*/
@Override
public void setArraySeparator(final String arraySeparator) { // NOPMD as this is implementing the abstract class
super.setArraySeparator(arraySeparator);
}
//---------------------------------------------------------------------
/**
* <p>Gets the content start text.</p>
*
* @return the current content start text
*/
@Override
public String getContentStart() { // NOPMD as this is implementing the abstract class
return super.getContentStart();
}
/**
* <p>Sets the content start text.</p>
*
* <p><code>null</code> is accepted, but will be converted
* to an empty String.</p>
*
* @param contentStart the new content start text
*/
@Override
public void setContentStart(final String contentStart) { // NOPMD as this is implementing the abstract class
super.setContentStart(contentStart);
}
//---------------------------------------------------------------------
/**
* <p>Gets the content end text.</p>
*
* @return the current content end text
*/
@Override
public String getContentEnd() { // NOPMD as this is implementing the abstract class
return super.getContentEnd();
}
/**
* <p>Sets the content end text.</p>
*
* <p><code>null</code> is accepted, but will be converted
* to an empty String.</p>
*
* @param contentEnd the new content end text
*/
@Override
public void setContentEnd(final String contentEnd) { // NOPMD as this is implementing the abstract class
super.setContentEnd(contentEnd);
}
//---------------------------------------------------------------------
/**
* <p>Gets the field name value separator text.</p>
*
* @return the current field name value separator text
*/
@Override
public String getFieldNameValueSeparator() { // NOPMD as this is implementing the abstract class
return super.getFieldNameValueSeparator();
}
/**
* <p>Sets the field name value separator text.</p>
*
* <p><code>null</code> is accepted, but will be converted
* to an empty String.</p>
*
* @param fieldNameValueSeparator the new field name value separator text
*/
@Override
public void setFieldNameValueSeparator(final String fieldNameValueSeparator) { // NOPMD as this is implementing the abstract class
super.setFieldNameValueSeparator(fieldNameValueSeparator);
}
//---------------------------------------------------------------------
/**
* <p>Gets the field separator text.</p>
*
* @return the current field separator text
*/
@Override
public String getFieldSeparator() { // NOPMD as this is implementing the abstract class
return super.getFieldSeparator();
}
/**
* <p>Sets the field separator text.</p>
*
* <p><code>null</code> is accepted, but will be converted
* to an empty String.</p>
*
* @param fieldSeparator the new field separator text
*/
@Override
public void setFieldSeparator(final String fieldSeparator) { // NOPMD as this is implementing the abstract class
super.setFieldSeparator(fieldSeparator);
}
//---------------------------------------------------------------------
/**
* <p>Gets whether the field separator should be added at the start
* of each buffer.</p>
*
* @return the fieldSeparatorAtStart flag
* @since 2.0
*/
@Override
public boolean isFieldSeparatorAtStart() { // NOPMD as this is implementing the abstract class
return super.isFieldSeparatorAtStart();
}
/**
* <p>Sets whether the field separator should be added at the start
* of each buffer.</p>
*
* @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
* @since 2.0
*/
@Override
public void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) { // NOPMD as this is implementing the abstract class
super.setFieldSeparatorAtStart(fieldSeparatorAtStart);
}
//---------------------------------------------------------------------
/**
* <p>Gets whether the field separator should be added at the end
* of each buffer.</p>
*
* @return fieldSeparatorAtEnd flag
* @since 2.0
*/
@Override
public boolean isFieldSeparatorAtEnd() { // NOPMD as this is implementing the abstract class
return super.isFieldSeparatorAtEnd();
}
/**
* <p>Sets whether the field separator should be added at the end
* of each buffer.</p>
*
* @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
* @since 2.0
*/
@Override
public void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) { // NOPMD as this is implementing the abstract class
super.setFieldSeparatorAtEnd(fieldSeparatorAtEnd);
}
//---------------------------------------------------------------------
/**
* <p>Gets the text to output when <code>null</code> found.</p>
*
* @return the current text to output when <code>null</code> found
*/
@Override
public String getNullText() { // NOPMD as this is implementing the abstract class
return super.getNullText();
}
/**
* <p>Sets the text to output when <code>null</code> found.</p>
*
* <p><code>null</code> is accepted, but will be converted
* to an empty String.</p>
*
* @param nullText the new text to output when <code>null</code> found
*/
@Override
public void setNullText(final String nullText) { // NOPMD as this is implementing the abstract class
super.setNullText(nullText);
}
//---------------------------------------------------------------------
/**
* <p>Gets the text to output when a <code>Collection</code>,
* <code>Map</code> or <code>Array</code> size is output.</p>
*
* <p>This is output before the size value.</p>
*
* @return the current start of size text
*/
@Override
public String getSizeStartText() { // NOPMD as this is implementing the abstract class
return super.getSizeStartText();
}
/**
* <p>Sets the start text to output when a <code>Collection</code>,
* <code>Map</code> or <code>Array</code> size is output.</p>
*
* <p>This is output before the size value.</p>
*
* <p><code>null</code> is accepted, but will be converted to
* an empty String.</p>
*
* @param sizeStartText the new start of size text
*/
@Override
public void setSizeStartText(final String sizeStartText) { // NOPMD as this is implementing the abstract class
super.setSizeStartText(sizeStartText);
}
//---------------------------------------------------------------------
/**
* <p>Gets the end text to output when a <code>Collection</code>,
* <code>Map</code> or <code>Array</code> size is output.</p>
*
* <p>This is output after the size value.</p>
*
* @return the current end of size text
*/
@Override
public String getSizeEndText() { // NOPMD as this is implementing the abstract class
return super.getSizeEndText();
}
/**
* <p>Sets the end text to output when a <code>Collection</code>,
* <code>Map</code> or <code>Array</code> size is output.</p>
*
* <p>This is output after the size value.</p>
*
* <p><code>null</code> is accepted, but will be converted
* to an empty String.</p>
*
* @param sizeEndText the new end of size text
*/
@Override
public void setSizeEndText(final String sizeEndText) { // NOPMD as this is implementing the abstract class
super.setSizeEndText(sizeEndText);
}
//---------------------------------------------------------------------
/**
* <p>Gets the start text to output when an <code>Object</code> is
* output in summary mode.</p>
*
* <P>This is output before the size value.</p>
*
* @return the current start of summary text
*/
@Override
public String getSummaryObjectStartText() { // NOPMD as this is implementing the abstract class
return super.getSummaryObjectStartText();
}
/**
* <p>Sets the start text to output when an <code>Object</code> is
* output in summary mode.</p>
*
* <p>This is output before the size value.</p>
*
* <p><code>null</code> is accepted, but will be converted to
* an empty String.</p>
*
* @param summaryObjectStartText the new start of summary text
*/
@Override
public void setSummaryObjectStartText(final String summaryObjectStartText) { // NOPMD as this is implementing the abstract class
super.setSummaryObjectStartText(summaryObjectStartText);
}
//---------------------------------------------------------------------
/**
* <p>Gets the end text to output when an <code>Object</code> is
* output in summary mode.</p>
*
* <p>This is output after the size value.</p>
*
* @return the current end of summary text
*/
@Override
public String getSummaryObjectEndText() { // NOPMD as this is implementing the abstract class
return super.getSummaryObjectEndText();
}
/**
* <p>Sets the end text to output when an <code>Object</code> is
* output in summary mode.</p>
*
* <p>This is output after the size value.</p>
*
* <p><code>null</code> is accepted, but will be converted to
* an empty String.</p>
*
* @param summaryObjectEndText the new end of summary text
*/
@Override
public void setSummaryObjectEndText(final String summaryObjectEndText) { // NOPMD as this is implementing the abstract class
super.setSummaryObjectEndText(summaryObjectEndText);
}
//---------------------------------------------------------------------
}

View File

@ -1,35 +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 com.qihoo360.replugin.ext.lang3.builder;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
/**
* Use this annotation to exclude a field from being being used by
* the {@link ReflectionToStringBuilder}.
*
* @since 3.5
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ToStringExclude {
}

View File

@ -1,69 +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 com.qihoo360.replugin.ext.lang3.concurrent;
/**
* <p>
* An exception class used for reporting error conditions related to accessing
* data of background tasks.
* </p>
* <p>
* The purpose of this exception class is analogous to the default JDK exception
* class {@link java.util.concurrent.ExecutionException}, i.e. it wraps an
* exception that occurred during the execution of a task. However, in contrast
* to {@code ExecutionException}, it wraps only checked exceptions. Runtime
* exceptions are thrown directly.
* </p>
*
* @since 3.0
*/
public class ConcurrentException extends Exception {
/**
* The serial version UID.
*/
private static final long serialVersionUID = 6622707671812226130L;
/**
* Creates a new, uninitialized instance of {@code ConcurrentException}.
*/
protected ConcurrentException() {
super();
}
/**
* Creates a new instance of {@code ConcurrentException} and initializes it
* with the given cause.
*
* @param cause the cause of this exception
* @throws IllegalArgumentException if the cause is not a checked exception
*/
public ConcurrentException(final Throwable cause) {
super(ConcurrentUtils.checkedException(cause));
}
/**
* Creates a new instance of {@code ConcurrentException} and initializes it
* with the given message and cause.
*
* @param msg the error message
* @param cause the cause of this exception
* @throws IllegalArgumentException if the cause is not a checked exception
*/
public ConcurrentException(final String msg, final Throwable cause) {
super(msg, ConcurrentUtils.checkedException(cause));
}
}

View File

@ -1,53 +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 com.qihoo360.replugin.ext.lang3.concurrent;
/**
* <p>
* Definition of an interface for the thread-safe initialization of objects.
* </p>
* <p>
* The idea behind this interface is to provide access to an object in a
* thread-safe manner. A {@code ConcurrentInitializer} can be passed to multiple
* threads which can all access the object produced by the initializer. Through
* the {@link #get()} method the object can be queried.
* </p>
* <p>
* Concrete implementations of this interface will use different strategies for
* the creation of the managed object, e.g. lazy initialization or
* initialization in a background thread. This is completely transparent to
* client code, so it is possible to change the initialization strategy without
* affecting clients.
* </p>
*
* @since 3.0
* @param <T> the type of the object managed by this initializer class
*/
public interface ConcurrentInitializer<T> {
/**
* Returns the fully initialized object produced by this {@code
* ConcurrentInitializer}. A concrete implementation here returns the
* results of the initialization process. This method may block until
* results are available. Typically, once created the result object is
* always the same.
*
* @return the object created by this {@code ConcurrentException}
* @throws ConcurrentException if an error occurred during initialization of
* the object
*/
T get() throws ConcurrentException;
}

View File

@ -1,71 +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 com.qihoo360.replugin.ext.lang3.concurrent;
/**
* <p>
* An exception class used for reporting runtime error conditions related to
* accessing data of background tasks.
* </p>
* <p>
* This class is an analogue of the {@link ConcurrentException} exception class.
* However, it is a runtime exception and thus does not need explicit catch
* clauses. Some methods of {@link ConcurrentUtils} throw {@code
* ConcurrentRuntimeException} exceptions rather than
* {@link ConcurrentException} exceptions. They can be used by client code that
* does not want to be bothered with checked exceptions.
* </p>
*
* @since 3.0
*/
public class ConcurrentRuntimeException extends RuntimeException {
/**
* The serial version UID.
*/
private static final long serialVersionUID = -6582182735562919670L;
/**
* Creates a new, uninitialized instance of {@code
* ConcurrentRuntimeException}.
*/
protected ConcurrentRuntimeException() {
super();
}
/**
* Creates a new instance of {@code ConcurrentRuntimeException} and
* initializes it with the given cause.
*
* @param cause the cause of this exception
* @throws IllegalArgumentException if the cause is not a checked exception
*/
public ConcurrentRuntimeException(final Throwable cause) {
super(ConcurrentUtils.checkedException(cause));
}
/**
* Creates a new instance of {@code ConcurrentRuntimeException} and
* initializes it with the given message and cause.
*
* @param msg the error message
* @param cause the cause of this exception
* @throws IllegalArgumentException if the cause is not a checked exception
*/
public ConcurrentRuntimeException(final String msg, final Throwable cause) {
super(msg, ConcurrentUtils.checkedException(cause));
}
}

View File

@ -1,392 +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 com.qihoo360.replugin.ext.lang3.concurrent;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.qihoo360.replugin.ext.lang3.Validate;
/**
* <p>
* An utility class providing functionality related to the {@code
* java.util.concurrent} package.
* </p>
*
* @since 3.0
*/
public class ConcurrentUtils {
/**
* Private constructor so that no instances can be created. This class
* contains only static utility methods.
*/
private ConcurrentUtils() {
}
/**
* Inspects the cause of the specified {@code ExecutionException} and
* creates a {@code ConcurrentException} with the checked cause if
* necessary. This method performs the following checks on the cause of the
* passed in exception:
* <ul>
* <li>If the passed in exception is <b>null</b> or the cause is
* <b>null</b>, this method returns <b>null</b>.</li>
* <li>If the cause is a runtime exception, it is directly thrown.</li>
* <li>If the cause is an error, it is directly thrown, too.</li>
* <li>In any other case the cause is a checked exception. The method then
* creates a {@link ConcurrentException}, initializes it with the cause, and
* returns it.</li>
* </ul>
*
* @param ex the exception to be processed
* @return a {@code ConcurrentException} with the checked cause
*/
public static ConcurrentException extractCause(final ExecutionException ex) {
if (ex == null || ex.getCause() == null) {
return null;
}
throwCause(ex);
return new ConcurrentException(ex.getMessage(), ex.getCause());
}
/**
* Inspects the cause of the specified {@code ExecutionException} and
* creates a {@code ConcurrentRuntimeException} with the checked cause if
* necessary. This method works exactly like
* {@link #extractCause(ExecutionException)}. The only difference is that
* the cause of the specified {@code ExecutionException} is extracted as a
* runtime exception. This is an alternative for client code that does not
* want to deal with checked exceptions.
*
* @param ex the exception to be processed
* @return a {@code ConcurrentRuntimeException} with the checked cause
*/
public static ConcurrentRuntimeException extractCauseUnchecked(
final ExecutionException ex) {
if (ex == null || ex.getCause() == null) {
return null;
}
throwCause(ex);
return new ConcurrentRuntimeException(ex.getMessage(), ex.getCause());
}
/**
* Handles the specified {@code ExecutionException}. This method calls
* {@link #extractCause(ExecutionException)} for obtaining the cause of the
* exception - which might already cause an unchecked exception or an error
* being thrown. If the cause is a checked exception however, it is wrapped
* in a {@code ConcurrentException}, which is thrown. If the passed in
* exception is <b>null</b> or has no cause, the method simply returns
* without throwing an exception.
*
* @param ex the exception to be handled
* @throws ConcurrentException if the cause of the {@code
* ExecutionException} is a checked exception
*/
public static void handleCause(final ExecutionException ex)
throws ConcurrentException {
final ConcurrentException cex = extractCause(ex);
if (cex != null) {
throw cex;
}
}
/**
* Handles the specified {@code ExecutionException} and transforms it into a
* runtime exception. This method works exactly like
* {@link #handleCause(ExecutionException)}, but instead of a
* {@link ConcurrentException} it throws a
* {@link ConcurrentRuntimeException}. This is an alternative for client
* code that does not want to deal with checked exceptions.
*
* @param ex the exception to be handled
* @throws ConcurrentRuntimeException if the cause of the {@code
* ExecutionException} is a checked exception; this exception is then
* wrapped in the thrown runtime exception
*/
public static void handleCauseUnchecked(final ExecutionException ex) {
final ConcurrentRuntimeException crex = extractCauseUnchecked(ex);
if (crex != null) {
throw crex;
}
}
/**
* Tests whether the specified {@code Throwable} is a checked exception. If
* not, an exception is thrown.
*
* @param ex the {@code Throwable} to check
* @return a flag whether the passed in exception is a checked exception
* @throws IllegalArgumentException if the {@code Throwable} is not a
* checked exception
*/
static Throwable checkedException(final Throwable ex) {
Validate.isTrue(ex != null && !(ex instanceof RuntimeException)
&& !(ex instanceof Error), "Not a checked exception: " + ex);
return ex;
}
/**
* Tests whether the cause of the specified {@code ExecutionException}
* should be thrown and does it if necessary.
*
* @param ex the exception in question
*/
private static void throwCause(final ExecutionException ex) {
if (ex.getCause() instanceof RuntimeException) {
throw (RuntimeException) ex.getCause();
}
if (ex.getCause() instanceof Error) {
throw (Error) ex.getCause();
}
}
//-----------------------------------------------------------------------
/**
* Invokes the specified {@code ConcurrentInitializer} and returns the
* object produced by the initializer. This method just invokes the {@code
* get()} method of the given {@code ConcurrentInitializer}. It is
* <b>null</b>-safe: if the argument is <b>null</b>, result is also
* <b>null</b>.
*
* @param <T> the type of the object produced by the initializer
* @param initializer the {@code ConcurrentInitializer} to be invoked
* @return the object managed by the {@code ConcurrentInitializer}
* @throws ConcurrentException if the {@code ConcurrentInitializer} throws
* an exception
*/
public static <T> T initialize(final ConcurrentInitializer<T> initializer)
throws ConcurrentException {
return initializer != null ? initializer.get() : null;
}
/**
* Invokes the specified {@code ConcurrentInitializer} and transforms
* occurring exceptions to runtime exceptions. This method works like
* {@link #initialize(ConcurrentInitializer)}, but if the {@code
* ConcurrentInitializer} throws a {@link ConcurrentException}, it is
* caught, and the cause is wrapped in a {@link ConcurrentRuntimeException}.
* So client code does not have to deal with checked exceptions.
*
* @param <T> the type of the object produced by the initializer
* @param initializer the {@code ConcurrentInitializer} to be invoked
* @return the object managed by the {@code ConcurrentInitializer}
* @throws ConcurrentRuntimeException if the initializer throws an exception
*/
public static <T> T initializeUnchecked(final ConcurrentInitializer<T> initializer) {
try {
return initialize(initializer);
} catch (final ConcurrentException cex) {
throw new ConcurrentRuntimeException(cex.getCause());
}
}
//-----------------------------------------------------------------------
/**
* <p>
* Puts a value in the specified {@code ConcurrentMap} if the key is not yet
* present. This method works similar to the {@code putIfAbsent()} method of
* the {@code ConcurrentMap} interface, but the value returned is different.
* Basically, this method is equivalent to the following code fragment:
* </p>
*
* <pre>
* if (!map.containsKey(key)) {
* map.put(key, value);
* return value;
* } else {
* return map.get(key);
* }
* </pre>
*
* <p>
* except that the action is performed atomically. So this method always
* returns the value which is stored in the map.
* </p>
* <p>
* This method is <b>null</b>-safe: It accepts a <b>null</b> map as input
* without throwing an exception. In this case the return value is
* <b>null</b>, too.
* </p>
*
* @param <K> the type of the keys of the map
* @param <V> the type of the values of the map
* @param map the map to be modified
* @param key the key of the value to be added
* @param value the value to be added
* @return the value stored in the map after this operation
*/
public static <K, V> V putIfAbsent(final ConcurrentMap<K, V> map, final K key, final V value) {
if (map == null) {
return null;
}
final V result = map.putIfAbsent(key, value);
return result != null ? result : value;
}
/**
* Checks if a concurrent map contains a key and creates a corresponding
* value if not. This method first checks the presence of the key in the
* given map. If it is already contained, its value is returned. Otherwise
* the {@code get()} method of the passed in {@link ConcurrentInitializer}
* is called. With the resulting object
* {@link #putIfAbsent(ConcurrentMap, Object, Object)} is called. This
* handles the case that in the meantime another thread has added the key to
* the map. Both the map and the initializer can be <b>null</b>; in this
* case this method simply returns <b>null</b>.
*
* @param <K> the type of the keys of the map
* @param <V> the type of the values of the map
* @param map the map to be modified
* @param key the key of the value to be added
* @param init the {@link ConcurrentInitializer} for creating the value
* @return the value stored in the map after this operation; this may or may
* not be the object created by the {@link ConcurrentInitializer}
* @throws ConcurrentException if the initializer throws an exception
*/
public static <K, V> V createIfAbsent(final ConcurrentMap<K, V> map, final K key,
final ConcurrentInitializer<V> init) throws ConcurrentException {
if (map == null || init == null) {
return null;
}
final V value = map.get(key);
if (value == null) {
return putIfAbsent(map, key, init.get());
}
return value;
}
/**
* Checks if a concurrent map contains a key and creates a corresponding
* value if not, suppressing checked exceptions. This method calls
* {@code createIfAbsent()}. If a {@link ConcurrentException} is thrown, it
* is caught and re-thrown as a {@link ConcurrentRuntimeException}.
*
* @param <K> the type of the keys of the map
* @param <V> the type of the values of the map
* @param map the map to be modified
* @param key the key of the value to be added
* @param init the {@link ConcurrentInitializer} for creating the value
* @return the value stored in the map after this operation; this may or may
* not be the object created by the {@link ConcurrentInitializer}
* @throws ConcurrentRuntimeException if the initializer throws an exception
*/
public static <K, V> V createIfAbsentUnchecked(final ConcurrentMap<K, V> map,
final K key, final ConcurrentInitializer<V> init) {
try {
return createIfAbsent(map, key, init);
} catch (final ConcurrentException cex) {
throw new ConcurrentRuntimeException(cex.getCause());
}
}
//-----------------------------------------------------------------------
/**
* <p>
* Gets an implementation of <code>Future</code> that is immediately done
* and returns the specified constant value.
* </p>
* <p>
* This can be useful to return a simple constant immediately from the
* concurrent processing, perhaps as part of avoiding nulls.
* A constant future can also be useful in testing.
* </p>
*
* @param <T> the type of the value used by this {@code Future} object
* @param value the constant value to return, may be null
* @return an instance of Future that will return the value, never null
*/
public static <T> Future<T> constantFuture(final T value) {
return new ConstantFuture<>(value);
}
/**
* A specialized {@code Future} implementation which wraps a constant value.
* @param <T> the type of the value wrapped by this class
*/
static final class ConstantFuture<T> implements Future<T> {
/** The constant value. */
private final T value;
/**
* Creates a new instance of {@code ConstantFuture} and initializes it
* with the constant value.
*
* @param value the value (may be <b>null</b>)
*/
ConstantFuture(final T value) {
this.value = value;
}
/**
* {@inheritDoc} This implementation always returns <b>true</b> because
* the constant object managed by this {@code Future} implementation is
* always available.
*/
@Override
public boolean isDone() {
return true;
}
/**
* {@inheritDoc} This implementation just returns the constant value.
*/
@Override
public T get() {
return value;
}
/**
* {@inheritDoc} This implementation just returns the constant value; it
* does not block, therefore the timeout has no meaning.
*/
@Override
public T get(final long timeout, final TimeUnit unit) {
return value;
}
/**
* {@inheritDoc} This implementation always returns <b>false</b>; there
* is no background process which could be cancelled.
*/
@Override
public boolean isCancelled() {
return false;
}
/**
* {@inheritDoc} The cancel operation is not supported. This
* implementation always returns <b>false</b>.
*/
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
return false;
}
}
}

View File

@ -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 com.qihoo360.replugin.ext.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);
}
}

View File

@ -1,265 +0,0 @@
/*
* Copyright (c) 2016, Liu Dong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.qihoo360.replugin.ext.lang3.exception;
import java.util.List;
import java.util.Set;
import com.qihoo360.replugin.ext.lang3.tuple.Pair;
/**
* <p>
* An exception that provides an easy and safe way to add contextual information.
* </p><p>
* An exception trace itself is often insufficient to provide rapid diagnosis of the issue.
* Frequently what is needed is a select few pieces of local contextual data.
* Providing this data is tricky however, due to concerns over formatting and nulls.
* </p><p>
* The contexted exception approach allows the exception to be created together with a
* list of context label-value pairs. This additional information is automatically included in
* the message and printed stack trace.
* </p><p>
* An unchecked version of this exception is provided by ContextedRuntimeException.
* </p>
* <p>
* To use this class write code as follows:
* </p>
* <pre>
* try {
* ...
* } catch (Exception e) {
* throw new ContextedException("Error posting account transaction", e)
* .addContextValue("Account Number", accountNumber)
* .addContextValue("Amount Posted", amountPosted)
* .addContextValue("Previous Balance", previousBalance)
* }
* }
* </pre>
* <p>
* or improve diagnose data at a higher level:
* </p>
* <pre>
* try {
* ...
* } catch (ContextedException e) {
* throw e.setContextValue("Transaction Id", transactionId);
* } catch (Exception e) {
* if (e instanceof ExceptionContext) {
* e.setContextValue("Transaction Id", transactionId);
* }
* throw e;
* }
* }
* </pre>
* <p>
* The output in a printStacktrace() (which often is written to a log) would look something like the following:
* </p>
* <pre>
* ContextedException: java.lang.Exception: Error posting account transaction
* Exception Context:
* [1:Account Number=null]
* [2:Amount Posted=100.00]
* [3:Previous Balance=-2.17]
* [4:Transaction Id=94ef1d15-d443-46c4-822b-637f26244899]
*
* ---------------------------------
* at org.apache.commons.lang3.exception.ContextedExceptionTest.testAddValue(ContextedExceptionTest.java:88)
* ..... (rest of trace)
* </pre>
*
* @see ContextedRuntimeException
* @since 3.0
*/
public class ContextedException extends Exception implements ExceptionContext {
/** The serialization version. */
private static final long serialVersionUID = 20110706L;
/** The context where the data is stored. */
private final ExceptionContext exceptionContext;
/**
* Instantiates ContextedException without message or cause.
* <p>
* The context information is stored using a default implementation.
*/
public ContextedException() {
super();
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedException with message, but without cause.
* <p>
* The context information is stored using a default implementation.
*
* @param message the exception message, may be null
*/
public ContextedException(final String message) {
super(message);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedException with cause, but without message.
* <p>
* The context information is stored using a default implementation.
*
* @param cause the underlying cause of the exception, may be null
*/
public ContextedException(final Throwable cause) {
super(cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedException with cause and message.
* <p>
* The context information is stored using a default implementation.
*
* @param message the exception message, may be null
* @param cause the underlying cause of the exception, may be null
*/
public ContextedException(final String message, final Throwable cause) {
super(message, cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedException with cause, message, and ExceptionContext.
*
* @param message the exception message, may be null
* @param cause the underlying cause of the exception, may be null
* @param context the context used to store the additional information, null uses default implementation
*/
public ContextedException(final String message, final Throwable cause, ExceptionContext context) {
super(message, cause);
if (context == null) {
context = new DefaultExceptionContext();
}
exceptionContext = context;
}
//-----------------------------------------------------------------------
/**
* Adds information helpful to a developer in diagnosing and correcting the problem.
* For the information to be meaningful, the value passed should have a reasonable
* toString() implementation.
* Different values can be added with the same label multiple times.
* <p>
* Note: This exception is only serializable if the object added is serializable.
* </p>
*
* @param label a textual label associated with information, {@code null} not recommended
* @param value information needed to understand exception, may be {@code null}
* @return {@code this}, for method chaining, not {@code null}
*/
@Override
public ContextedException addContextValue(final String label, final Object value) {
exceptionContext.addContextValue(label, value);
return this;
}
/**
* Sets information helpful to a developer in diagnosing and correcting the problem.
* For the information to be meaningful, the value passed should have a reasonable
* toString() implementation.
* Any existing values with the same labels are removed before the new one is added.
* <p>
* Note: This exception is only serializable if the object added as value is serializable.
* </p>
*
* @param label a textual label associated with information, {@code null} not recommended
* @param value information needed to understand exception, may be {@code null}
* @return {@code this}, for method chaining, not {@code null}
*/
@Override
public ContextedException setContextValue(final String label, final Object value) {
exceptionContext.setContextValue(label, value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public List<Object> getContextValues(final String label) {
return this.exceptionContext.getContextValues(label);
}
/**
* {@inheritDoc}
*/
@Override
public Object getFirstContextValue(final String label) {
return this.exceptionContext.getFirstContextValue(label);
}
/**
* {@inheritDoc}
*/
@Override
public List<Pair<String, Object>> getContextEntries() {
return this.exceptionContext.getContextEntries();
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getContextLabels() {
return exceptionContext.getContextLabels();
}
/**
* Provides the message explaining the exception, including the contextual data.
*
* @see Throwable#getMessage()
* @return the message, never null
*/
@Override
public String getMessage(){
return getFormattedExceptionMessage(super.getMessage());
}
/**
* Provides the message explaining the exception without the contextual data.
*
* @see Throwable#getMessage()
* @return the message
* @since 3.0.1
*/
public String getRawMessage() {
return super.getMessage();
}
/**
* {@inheritDoc}
*/
@Override
public String getFormattedExceptionMessage(final String baseMessage) {
return exceptionContext.getFormattedExceptionMessage(baseMessage);
}
}

View File

@ -1,266 +0,0 @@
/*
* Copyright (c) 2016, Liu Dong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.qihoo360.replugin.ext.lang3.exception;
import java.util.List;
import java.util.Set;
import com.qihoo360.replugin.ext.lang3.tuple.Pair;
/**
* <p>
* A runtime exception that provides an easy and safe way to add contextual information.
* </p><p>
* An exception trace itself is often insufficient to provide rapid diagnosis of the issue.
* Frequently what is needed is a select few pieces of local contextual data.
* Providing this data is tricky however, due to concerns over formatting and nulls.
* </p><p>
* The contexted exception approach allows the exception to be created together with a
* list of context label-value pairs. This additional information is automatically included in
* the message and printed stack trace.
* </p><p>
* A checked version of this exception is provided by ContextedException.
* </p>
* <p>
* To use this class write code as follows:
* </p>
* <pre>
* try {
* ...
* } catch (Exception e) {
* throw new ContextedRuntimeException("Error posting account transaction", e)
* .addContextValue("Account Number", accountNumber)
* .addContextValue("Amount Posted", amountPosted)
* .addContextValue("Previous Balance", previousBalance)
* }
* }
* </pre>
* <p>
* or improve diagnose data at a higher level:
* </p>
* <pre>
* try {
* ...
* } catch (ContextedRuntimeException e) {
* throw e.setContextValue("Transaction Id", transactionId);
* } catch (Exception e) {
* if (e instanceof ExceptionContext) {
* e.setContextValue("Transaction Id", transactionId);
* }
* throw e;
* }
* }
* </pre>
* <p>
* The output in a printStacktrace() (which often is written to a log) would look something like the following:
* </p>
* <pre>
* ContextedRuntimeException: java.lang.Exception: Error posting account transaction
* Exception Context:
* [1:Account Number=null]
* [2:Amount Posted=100.00]
* [3:Previous Balance=-2.17]
* [4:Transaction Id=94ef1d15-d443-46c4-822b-637f26244899]
*
* ---------------------------------
* at org.apache.commons.lang3.exception.ContextedRuntimeExceptionTest.testAddValue(ContextedExceptionTest.java:88)
* ..... (rest of trace)
* </pre>
*
* @see ContextedException
* @since 3.0
*/
public class ContextedRuntimeException extends RuntimeException implements ExceptionContext {
/** The serialization version. */
private static final long serialVersionUID = 20110706L;
/** The context where the data is stored. */
private final ExceptionContext exceptionContext;
/**
* Instantiates ContextedRuntimeException without message or cause.
* <p>
* The context information is stored using a default implementation.
*/
public ContextedRuntimeException() {
super();
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedRuntimeException with message, but without cause.
* <p>
* The context information is stored using a default implementation.
*
* @param message the exception message, may be null
*/
public ContextedRuntimeException(final String message) {
super(message);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedRuntimeException with cause, but without message.
* <p>
* The context information is stored using a default implementation.
*
* @param cause the underlying cause of the exception, may be null
*/
public ContextedRuntimeException(final Throwable cause) {
super(cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedRuntimeException with cause and message.
* <p>
* The context information is stored using a default implementation.
*
* @param message the exception message, may be null
* @param cause the underlying cause of the exception, may be null
*/
public ContextedRuntimeException(final String message, final Throwable cause) {
super(message, cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedRuntimeException with cause, message, and ExceptionContext.
*
* @param message the exception message, may be null
* @param cause the underlying cause of the exception, may be null
* @param context the context used to store the additional information, null uses default implementation
*/
public ContextedRuntimeException(final String message, final Throwable cause, ExceptionContext context) {
super(message, cause);
if (context == null) {
context = new DefaultExceptionContext();
}
exceptionContext = context;
}
//-----------------------------------------------------------------------
/**
* Adds information helpful to a developer in diagnosing and correcting the problem.
* For the information to be meaningful, the value passed should have a reasonable
* toString() implementation.
* Different values can be added with the same label multiple times.
* <p>
* Note: This exception is only serializable if the object added is serializable.
* </p>
*
* @param label a textual label associated with information, {@code null} not recommended
* @param value information needed to understand exception, may be {@code null}
* @return {@code this}, for method chaining, not {@code null}
*/
@Override
public ContextedRuntimeException addContextValue(final String label, final Object value) {
exceptionContext.addContextValue(label, value);
return this;
}
/**
* Sets information helpful to a developer in diagnosing and correcting the problem.
* For the information to be meaningful, the value passed should have a reasonable
* toString() implementation.
* Any existing values with the same labels are removed before the new one is added.
* <p>
* Note: This exception is only serializable if the object added as value is serializable.
* </p>
*
* @param label a textual label associated with information, {@code null} not recommended
* @param value information needed to understand exception, may be {@code null}
* @return {@code this}, for method chaining, not {@code null}
*/
@Override
public ContextedRuntimeException setContextValue(final String label, final Object value) {
exceptionContext.setContextValue(label, value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public List<Object> getContextValues(final String label) {
return this.exceptionContext.getContextValues(label);
}
/**
* {@inheritDoc}
*/
@Override
public Object getFirstContextValue(final String label) {
return this.exceptionContext.getFirstContextValue(label);
}
/**
* {@inheritDoc}
*/
@Override
public List<Pair<String, Object>> getContextEntries() {
return this.exceptionContext.getContextEntries();
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getContextLabels() {
return exceptionContext.getContextLabels();
}
/**
* Provides the message explaining the exception, including the contextual data.
*
* @see Throwable#getMessage()
* @return the message, never null
*/
@Override
public String getMessage(){
return getFormattedExceptionMessage(super.getMessage());
}
/**
* Provides the message explaining the exception without the contextual data.
*
* @see Throwable#getMessage()
* @return the message
* @since 3.0.1
*/
public String getRawMessage() {
return super.getMessage();
}
/**
* {@inheritDoc}
*/
@Override
public String getFormattedExceptionMessage(final String baseMessage) {
return exceptionContext.getFormattedExceptionMessage(baseMessage);
}
}

View File

@ -1,165 +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 com.qihoo360.replugin.ext.lang3.exception;
import com.qihoo360.replugin.ext.lang3.StringUtils;
import com.qihoo360.replugin.ext.lang3.tuple.ImmutablePair;
import com.qihoo360.replugin.ext.lang3.tuple.Pair;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Default implementation of the context storing the label-value pairs for contexted exceptions.
* <p>
* This implementation is serializable, however this is dependent on the values that
* are added also being serializable.
* </p>
*
* @see ContextedException
* @see ContextedRuntimeException
* @since 3.0
*/
public class DefaultExceptionContext implements ExceptionContext, Serializable {
/** The serialization version. */
private static final long serialVersionUID = 20110706L;
/** The list storing the label-data pairs. */
private final List<Pair<String, Object>> contextValues = new ArrayList<>();
/**
* {@inheritDoc}
*/
@Override
public DefaultExceptionContext addContextValue(final String label, final Object value) {
contextValues.add(new ImmutablePair<>(label, value));
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DefaultExceptionContext setContextValue(final String label, final Object value) {
for (final Iterator<Pair<String, Object>> iter = contextValues.iterator(); iter.hasNext();) {
final Pair<String, Object> p = iter.next();
if (StringUtils.equals(label, p.getKey())) {
iter.remove();
}
}
addContextValue(label, value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public List<Object> getContextValues(final String label) {
final List<Object> values = new ArrayList<>();
for (final Pair<String, Object> pair : contextValues) {
if (StringUtils.equals(label, pair.getKey())) {
values.add(pair.getValue());
}
}
return values;
}
/**
* {@inheritDoc}
*/
@Override
public Object getFirstContextValue(final String label) {
for (final Pair<String, Object> pair : contextValues) {
if (StringUtils.equals(label, pair.getKey())) {
return pair.getValue();
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getContextLabels() {
final Set<String> labels = new HashSet<>();
for (final Pair<String, Object> pair : contextValues) {
labels.add(pair.getKey());
}
return labels;
}
/**
* {@inheritDoc}
*/
@Override
public List<Pair<String, Object>> getContextEntries() {
return contextValues;
}
/**
* Builds the message containing the contextual information.
*
* @param baseMessage the base exception message <b>without</b> context information appended
* @return the exception message <b>with</b> context information appended, never null
*/
@Override
public String getFormattedExceptionMessage(final String baseMessage){
final StringBuilder buffer = new StringBuilder(256);
if (baseMessage != null) {
buffer.append(baseMessage);
}
if (contextValues.size() > 0) {
if (buffer.length() > 0) {
buffer.append('\n');
}
buffer.append("Exception Context:\n");
int i = 0;
for (final Pair<String, Object> pair : contextValues) {
buffer.append("\t[");
buffer.append(++i);
buffer.append(':');
buffer.append(pair.getKey());
buffer.append("=");
final Object value = pair.getValue();
if (value == null) {
buffer.append("null");
} else {
String valueStr;
try {
valueStr = value.toString();
} catch (final Exception e) {
valueStr = "Exception thrown on toString(): " + ExceptionUtils.getStackTrace(e);
}
buffer.append(valueStr);
}
buffer.append("]\n");
}
buffer.append("---------------------------------");
}
return buffer.toString();
}
}

View File

@ -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 com.qihoo360.replugin.ext.lang3.exception;
import com.qihoo360.replugin.ext.lang3.tuple.Pair;
import java.util.List;
import java.util.Set;
/**
* Allows the storage and retrieval of contextual information based on label-value
* pairs for exceptions.
* <p>
* Implementations are expected to manage the pairs in a list-style collection
* that keeps the pairs in the sequence of their addition.
* </p>
*
* @see ContextedException
* @see ContextedRuntimeException
* @since 3.0
*/
public interface ExceptionContext {
/**
* Adds a contextual label-value pair into this context.
* <p>
* The pair will be added to the context, independently of an already
* existing pair with the same label.
* </p>
*
* @param label the label of the item to add, {@code null} not recommended
* @param value the value of item to add, may be {@code null}
* @return {@code this}, for method chaining, not {@code null}
*/
ExceptionContext addContextValue(String label, Object value);
/**
* Sets a contextual label-value pair into this context.
* <p>
* The pair will be added normally, but any existing label-value pair with
* the same label is removed from the context.
* </p>
*
* @param label the label of the item to add, {@code null} not recommended
* @param value the value of item to add, may be {@code null}
* @return {@code this}, for method chaining, not {@code null}
*/
ExceptionContext setContextValue(String label, Object value);
/**
* Retrieves all the contextual data values associated with the label.
*
* @param label the label to get the contextual values for, may be {@code null}
* @return the contextual values associated with the label, never {@code null}
*/
List<Object> getContextValues(String label);
/**
* Retrieves the first available contextual data value associated with the label.
*
* @param label the label to get the contextual value for, may be {@code null}
* @return the first contextual value associated with the label, may be {@code null}
*/
Object getFirstContextValue(String label);
/**
* Retrieves the full set of labels defined in the contextual data.
*
* @return the set of labels, not {@code null}
*/
Set<String> getContextLabels();
/**
* Retrieves the full list of label-value pairs defined in the contextual data.
*
* @return the list of pairs, not {@code null}
*/
List<Pair<String, Object>> getContextEntries();
/**
* Gets the contextualized error message based on a base message.
* This will add the context label-value pairs to the message.
*
* @param baseMessage the base exception message <b>without</b> context information appended
* @return the exception message <b>with</b> context information appended, not {@code null}
*/
String getFormattedExceptionMessage(String baseMessage);
}

View File

@ -1,824 +0,0 @@
/*
* Copyright (c) 2016, Liu Dong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.qihoo360.replugin.ext.lang3.exception;
import com.qihoo360.replugin.ext.lang3.ArrayUtils;
import com.qihoo360.replugin.ext.lang3.StringUtils;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import com.qihoo360.replugin.ext.lang3.ClassUtils;
import com.qihoo360.replugin.ext.lang3.Validate;
/**
* <p>Provides utilities for manipulating and examining
* <code>Throwable</code> objects.</p>
*
* @since 1.0
*/
public class ExceptionUtils {
/**
* <p>Used when printing stack frames to denote the start of a
* wrapped exception.</p>
*
* <p>Package private for accessibility by test suite.</p>
*/
static final String WRAPPED_MARKER = " [wrapped] ";
/**
* <p>The names of methods commonly used to access a wrapped exception.</p>
*/
// TODO: Remove in Lang 4.0
private static final String[] CAUSE_METHOD_NAMES = {
"getCause",
"getNextException",
"getTargetException",
"getException",
"getSourceException",
"getRootCause",
"getCausedByException",
"getNested",
"getLinkedException",
"getNestedException",
"getLinkedCause",
"getThrowable",
};
/**
* <p>
* Public constructor allows an instance of <code>ExceptionUtils</code> to be created, although that is not
* normally necessary.
* </p>
*/
public ExceptionUtils() {
super();
}
//-----------------------------------------------------------------------
/**
* <p>Returns the default names used when searching for the cause of an exception.</p>
*
* <p>This may be modified and used in the overloaded getCause(Throwable, String[]) method.</p>
*
* @return cloned array of the default method names
* @since 3.0
* @deprecated This feature will be removed in Lang 4.0
*/
@Deprecated
public static String[] getDefaultCauseMethodNames() {
return ArrayUtils.clone(CAUSE_METHOD_NAMES);
}
//-----------------------------------------------------------------------
/**
* <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
*
* <p>The method searches for methods with specific names that return a
* <code>Throwable</code> object. This will pick up most wrapping exceptions,
* including those from JDK 1.4.
*
* <p>The default list searched for are:</p>
* <ul>
* <li><code>getCause()</code></li>
* <li><code>getNextException()</code></li>
* <li><code>getTargetException()</code></li>
* <li><code>getException()</code></li>
* <li><code>getSourceException()</code></li>
* <li><code>getRootCause()</code></li>
* <li><code>getCausedByException()</code></li>
* <li><code>getNested()</code></li>
* </ul>
*
* <p>If none of the above is found, returns <code>null</code>.</p>
*
* @param throwable the throwable to introspect for a cause, may be null
* @return the cause of the <code>Throwable</code>,
* <code>null</code> if none found or null throwable input
* @since 1.0
* @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead
*/
@Deprecated
public static Throwable getCause(final Throwable throwable) {
return getCause(throwable, null);
}
/**
* <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
*
* <p>A <code>null</code> set of method names means use the default set.
* A <code>null</code> in the set of method names will be ignored.</p>
*
* @param throwable the throwable to introspect for a cause, may be null
* @param methodNames the method names, null treated as default set
* @return the cause of the <code>Throwable</code>,
* <code>null</code> if none found or null throwable input
* @since 1.0
* @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead
*/
@Deprecated
public static Throwable getCause(final Throwable throwable, String[] methodNames) {
if (throwable == null) {
return null;
}
if (methodNames == null) {
final Throwable cause = throwable.getCause();
if (cause != null) {
return cause;
}
methodNames = CAUSE_METHOD_NAMES;
}
for (final String methodName : methodNames) {
if (methodName != null) {
final Throwable legacyCause = getCauseUsingMethodName(throwable, methodName);
if (legacyCause != null) {
return legacyCause;
}
}
}
return null;
}
/**
* <p>Introspects the <code>Throwable</code> to obtain the root cause.</p>
*
* <p>This method walks through the exception chain to the last element,
* "root" of the tree, using {@link #getCause(Throwable)}, and
* returns that exception.</p>
*
* <p>From version 2.2, this method handles recursive cause structures
* that might otherwise cause infinite loops. If the throwable parameter
* has a cause of itself, then null will be returned. If the throwable
* parameter cause chain loops, the last element in the chain before the
* loop is returned.</p>
*
* @param throwable the throwable to get the root cause for, may be null
* @return the root cause of the <code>Throwable</code>,
* <code>null</code> if none found or null throwable input
*/
public static Throwable getRootCause(final Throwable throwable) {
final List<Throwable> list = getThrowableList(throwable);
return list.size() < 2 ? null : list.get(list.size() - 1);
}
/**
* <p>Finds a <code>Throwable</code> by method name.</p>
*
* @param throwable the exception to examine
* @param methodName the name of the method to find and invoke
* @return the wrapped exception, or <code>null</code> if not found
*/
// TODO: Remove in Lang 4.0
private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) {
Method method = null;
try {
method = throwable.getClass().getMethod(methodName);
} catch (final NoSuchMethodException | SecurityException ignored) { // NOPMD
// exception ignored
}
if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) {
try {
return (Throwable) method.invoke(throwable);
} catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException ignored) { // NOPMD
// exception ignored
}
}
return null;
}
//-----------------------------------------------------------------------
/**
* <p>Counts the number of <code>Throwable</code> objects in the
* exception chain.</p>
*
* <p>A throwable without cause will return <code>1</code>.
* A throwable with one cause will return <code>2</code> and so on.
* A <code>null</code> throwable will return <code>0</code>.</p>
*
* <p>From version 2.2, this method handles recursive cause structures
* that might otherwise cause infinite loops. The cause chain is
* processed until the end is reached, or until the next item in the
* chain is already in the result set.</p>
*
* @param throwable the throwable to inspect, may be null
* @return the count of throwables, zero if null input
*/
public static int getThrowableCount(final Throwable throwable) {
return getThrowableList(throwable).size();
}
/**
* <p>Returns the list of <code>Throwable</code> objects in the
* exception chain.</p>
*
* <p>A throwable without cause will return an array containing
* one element - the input throwable.
* A throwable with one cause will return an array containing
* two elements. - the input throwable and the cause throwable.
* A <code>null</code> throwable will return an array of size zero.</p>
*
* <p>From version 2.2, this method handles recursive cause structures
* that might otherwise cause infinite loops. The cause chain is
* processed until the end is reached, or until the next item in the
* chain is already in the result set.</p>
*
* @see #getThrowableList(Throwable)
* @param throwable the throwable to inspect, may be null
* @return the array of throwables, never null
*/
public static Throwable[] getThrowables(final Throwable throwable) {
final List<Throwable> list = getThrowableList(throwable);
return list.toArray(new Throwable[list.size()]);
}
/**
* <p>Returns the list of <code>Throwable</code> objects in the
* exception chain.</p>
*
* <p>A throwable without cause will return a list containing
* one element - the input throwable.
* A throwable with one cause will return a list containing
* two elements. - the input throwable and the cause throwable.
* A <code>null</code> throwable will return a list of size zero.</p>
*
* <p>This method handles recursive cause structures that might
* otherwise cause infinite loops. The cause chain is processed until
* the end is reached, or until the next item in the chain is already
* in the result set.</p>
*
* @param throwable the throwable to inspect, may be null
* @return the list of throwables, never null
* @since Commons Lang 2.2
*/
public static List<Throwable> getThrowableList(Throwable throwable) {
final List<Throwable> list = new ArrayList<>();
while (throwable != null && list.contains(throwable) == false) {
list.add(throwable);
throwable = ExceptionUtils.getCause(throwable);
}
return list;
}
//-----------------------------------------------------------------------
/**
* <p>Returns the (zero based) index of the first <code>Throwable</code>
* that matches the specified class (exactly) in the exception chain.
* Subclasses of the specified class do not match - see
* {@link #indexOfType(Throwable, Class)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.</p>
*
* @param throwable the throwable to inspect, may be null
* @param clazz the class to search for, subclasses do not match, null returns -1
* @return the index into the throwable chain, -1 if no match or null input
*/
public static int indexOfThrowable(final Throwable throwable, final Class<?> clazz) {
return indexOf(throwable, clazz, 0, false);
}
/**
* <p>Returns the (zero based) index of the first <code>Throwable</code>
* that matches the specified type in the exception chain from
* a specified index.
* Subclasses of the specified class do not match - see
* {@link #indexOfType(Throwable, Class, int)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.
* A negative start index is treated as zero.
* A start index greater than the number of throwables returns <code>-1</code>.</p>
*
* @param throwable the throwable to inspect, may be null
* @param clazz the class to search for, subclasses do not match, null returns -1
* @param fromIndex the (zero based) index of the starting position,
* negative treated as zero, larger than chain size returns -1
* @return the index into the throwable chain, -1 if no match or null input
*/
public static int indexOfThrowable(final Throwable throwable, final Class<?> clazz, final int fromIndex) {
return indexOf(throwable, clazz, fromIndex, false);
}
//-----------------------------------------------------------------------
/**
* <p>Returns the (zero based) index of the first <code>Throwable</code>
* that matches the specified class or subclass in the exception chain.
* Subclasses of the specified class do match - see
* {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.</p>
*
* @param throwable the throwable to inspect, may be null
* @param type the type to search for, subclasses match, null returns -1
* @return the index into the throwable chain, -1 if no match or null input
* @since 2.1
*/
public static int indexOfType(final Throwable throwable, final Class<?> type) {
return indexOf(throwable, type, 0, true);
}
/**
* <p>Returns the (zero based) index of the first <code>Throwable</code>
* that matches the specified type in the exception chain from
* a specified index.
* Subclasses of the specified class do match - see
* {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
*
* <p>A <code>null</code> throwable returns <code>-1</code>.
* A <code>null</code> type returns <code>-1</code>.
* No match in the chain returns <code>-1</code>.
* A negative start index is treated as zero.
* A start index greater than the number of throwables returns <code>-1</code>.</p>
*
* @param throwable the throwable to inspect, may be null
* @param type the type to search for, subclasses match, null returns -1
* @param fromIndex the (zero based) index of the starting position,
* negative treated as zero, larger than chain size returns -1
* @return the index into the throwable chain, -1 if no match or null input
* @since 2.1
*/
public static int indexOfType(final Throwable throwable, final Class<?> type, final int fromIndex) {
return indexOf(throwable, type, fromIndex, true);
}
/**
* <p>Worker method for the <code>indexOfType</code> methods.</p>
*
* @param throwable the throwable to inspect, may be null
* @param type the type to search for, subclasses match, null returns -1
* @param fromIndex the (zero based) index of the starting position,
* negative treated as zero, larger than chain size returns -1
* @param subclass if <code>true</code>, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
* using references
* @return index of the <code>type</code> within throwables nested within the specified <code>throwable</code>
*/
private static int indexOf(final Throwable throwable, final Class<?> type, int fromIndex, final boolean subclass) {
if (throwable == null || type == null) {
return -1;
}
if (fromIndex < 0) {
fromIndex = 0;
}
final Throwable[] throwables = ExceptionUtils.getThrowables(throwable);
if (fromIndex >= throwables.length) {
return -1;
}
if (subclass) {
for (int i = fromIndex; i < throwables.length; i++) {
if (type.isAssignableFrom(throwables[i].getClass())) {
return i;
}
}
} else {
for (int i = fromIndex; i < throwables.length; i++) {
if (type.equals(throwables[i].getClass())) {
return i;
}
}
}
return -1;
}
//-----------------------------------------------------------------------
/**
* <p>Prints a compact stack trace for the root cause of a throwable
* to <code>System.err</code>.</p>
*
* <p>The compact stack trace starts with the root cause and prints
* stack frames up to the place where it was caught and wrapped.
* Then it prints the wrapped exception and continues with stack frames
* until the wrapper exception is caught and wrapped again, etc.</p>
*
* <p>The output of this method is consistent across JDK versions.
* Note that this is the opposite order to the JDK1.4 display.</p>
*
* <p>The method is equivalent to <code>printStackTrace</code> for throwables
* that don't have nested causes.</p>
*
* @param throwable the throwable to output
* @since 2.0
*/
public static void printRootCauseStackTrace(final Throwable throwable) {
printRootCauseStackTrace(throwable, System.err);
}
/**
* <p>Prints a compact stack trace for the root cause of a throwable.</p>
*
* <p>The compact stack trace starts with the root cause and prints
* stack frames up to the place where it was caught and wrapped.
* Then it prints the wrapped exception and continues with stack frames
* until the wrapper exception is caught and wrapped again, etc.</p>
*
* <p>The output of this method is consistent across JDK versions.
* Note that this is the opposite order to the JDK1.4 display.</p>
*
* <p>The method is equivalent to <code>printStackTrace</code> for throwables
* that don't have nested causes.</p>
*
* @param throwable the throwable to output, may be null
* @param stream the stream to output to, may not be null
* @throws IllegalArgumentException if the stream is <code>null</code>
* @since 2.0
*/
public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream stream) {
if (throwable == null) {
return;
}
Validate.isTrue(stream != null, "The PrintStream must not be null");
final String trace[] = getRootCauseStackTrace(throwable);
for (final String element : trace) {
stream.println(element);
}
stream.flush();
}
/**
* <p>Prints a compact stack trace for the root cause of a throwable.</p>
*
* <p>The compact stack trace starts with the root cause and prints
* stack frames up to the place where it was caught and wrapped.
* Then it prints the wrapped exception and continues with stack frames
* until the wrapper exception is caught and wrapped again, etc.</p>
*
* <p>The output of this method is consistent across JDK versions.
* Note that this is the opposite order to the JDK1.4 display.</p>
*
* <p>The method is equivalent to <code>printStackTrace</code> for throwables
* that don't have nested causes.</p>
*
* @param throwable the throwable to output, may be null
* @param writer the writer to output to, may not be null
* @throws IllegalArgumentException if the writer is <code>null</code>
* @since 2.0
*/
public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter writer) {
if (throwable == null) {
return;
}
Validate.isTrue(writer != null, "The PrintWriter must not be null");
final String trace[] = getRootCauseStackTrace(throwable);
for (final String element : trace) {
writer.println(element);
}
writer.flush();
}
//-----------------------------------------------------------------------
/**
* <p>Creates a compact stack trace for the root cause of the supplied
* <code>Throwable</code>.</p>
*
* <p>The output of this method is consistent across JDK versions.
* It consists of the root exception followed by each of its wrapping
* exceptions separated by '[wrapped]'. Note that this is the opposite
* order to the JDK1.4 display.</p>
*
* @param throwable the throwable to examine, may be null
* @return an array of stack trace frames, never null
* @since 2.0
*/
public static String[] getRootCauseStackTrace(final Throwable throwable) {
if (throwable == null) {
return ArrayUtils.EMPTY_STRING_ARRAY;
}
final Throwable throwables[] = getThrowables(throwable);
final int count = throwables.length;
final List<String> frames = new ArrayList<>();
List<String> nextTrace = getStackFrameList(throwables[count - 1]);
for (int i = count; --i >= 0;) {
final List<String> trace = nextTrace;
if (i != 0) {
nextTrace = getStackFrameList(throwables[i - 1]);
removeCommonFrames(trace, nextTrace);
}
if (i == count - 1) {
frames.add(throwables[i].toString());
} else {
frames.add(WRAPPED_MARKER + throwables[i].toString());
}
for (int j = 0; j < trace.size(); j++) {
frames.add(trace.get(j));
}
}
return frames.toArray(new String[frames.size()]);
}
/**
* <p>Removes common frames from the cause trace given the two stack traces.</p>
*
* @param causeFrames stack trace of a cause throwable
* @param wrapperFrames stack trace of a wrapper throwable
* @throws IllegalArgumentException if either argument is null
* @since 2.0
*/
public static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) {
if (causeFrames == null || wrapperFrames == null) {
throw new IllegalArgumentException("The List must not be null");
}
int causeFrameIndex = causeFrames.size() - 1;
int wrapperFrameIndex = wrapperFrames.size() - 1;
while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
// Remove the frame from the cause trace if it is the same
// as in the wrapper trace
final String causeFrame = causeFrames.get(causeFrameIndex);
final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);
if (causeFrame.equals(wrapperFrame)) {
causeFrames.remove(causeFrameIndex);
}
causeFrameIndex--;
wrapperFrameIndex--;
}
}
//-----------------------------------------------------------------------
/**
* <p>Gets the stack trace from a Throwable as a String.</p>
*
* <p>The result of this method vary by JDK version as this method
* uses {@link Throwable#printStackTrace(PrintWriter)}.
* On JDK1.3 and earlier, the cause exception will not be shown
* unless the specified throwable alters printStackTrace.</p>
*
* @param throwable the <code>Throwable</code> to be examined
* @return the stack trace as generated by the exception's
* <code>printStackTrace(PrintWriter)</code> method
*/
public static String getStackTrace(final Throwable throwable) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}
/**
* <p>Captures the stack trace associated with the specified
* <code>Throwable</code> object, decomposing it into a list of
* stack frames.</p>
*
* <p>The result of this method vary by JDK version as this method
* uses {@link Throwable#printStackTrace(PrintWriter)}.
* On JDK1.3 and earlier, the cause exception will not be shown
* unless the specified throwable alters printStackTrace.</p>
*
* @param throwable the <code>Throwable</code> to examine, may be null
* @return an array of strings describing each stack frame, never null
*/
public static String[] getStackFrames(final Throwable throwable) {
if (throwable == null) {
return ArrayUtils.EMPTY_STRING_ARRAY;
}
return getStackFrames(getStackTrace(throwable));
}
//-----------------------------------------------------------------------
/**
* <p>Returns an array where each element is a line from the argument.</p>
*
* <p>The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.</p>
*
* @param stackTrace a stack trace String
* @return an array where each element is a line from the argument
*/
static String[] getStackFrames(final String stackTrace) {
final String linebreak = System.lineSeparator();
final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
final List<String> list = new ArrayList<>();
while (frames.hasMoreTokens()) {
list.add(frames.nextToken());
}
return list.toArray(new String[list.size()]);
}
/**
* <p>Produces a <code>List</code> of stack frames - the message
* is not included. Only the trace of the specified exception is
* returned, any caused by trace is stripped.</p>
*
* <p>This works in most cases - it will only fail if the exception
* message contains a line that starts with:
* <code>&quot;&nbsp;&nbsp;&nbsp;at&quot;.</code></p>
*
* @param t is any throwable
* @return List of stack frames
*/
static List<String> getStackFrameList(final Throwable t) {
final String stackTrace = getStackTrace(t);
final String linebreak = System.lineSeparator();
final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
final List<String> list = new ArrayList<>();
boolean traceStarted = false;
while (frames.hasMoreTokens()) {
final String token = frames.nextToken();
// Determine if the line starts with <whitespace>at
final int at = token.indexOf("at");
if (at != -1 && token.substring(0, at).trim().isEmpty()) {
traceStarted = true;
list.add(token);
} else if (traceStarted) {
break;
}
}
return list;
}
//-----------------------------------------------------------------------
/**
* Gets a short message summarising the exception.
* <p>
* The message returned is of the form
* {ClassNameWithoutPackage}: {ThrowableMessage}
*
* @param th the throwable to get a message for, null returns empty string
* @return the message, non-null
* @since Commons Lang 2.2
*/
public static String getMessage(final Throwable th) {
if (th == null) {
return StringUtils.EMPTY;
}
final String clsName = ClassUtils.getShortClassName(th, null);
final String msg = th.getMessage();
return clsName + ": " + StringUtils.defaultString(msg);
}
//-----------------------------------------------------------------------
/**
* Gets a short message summarising the root cause exception.
* <p>
* The message returned is of the form
* {ClassNameWithoutPackage}: {ThrowableMessage}
*
* @param th the throwable to get a message for, null returns empty string
* @return the message, non-null
* @since Commons Lang 2.2
*/
public static String getRootCauseMessage(final Throwable th) {
Throwable root = ExceptionUtils.getRootCause(th);
root = root == null ? th : root;
return getMessage(root);
}
/**
* Throw a checked exception without adding the exception to the throws
* clause of the calling method. This method prevents throws clause
* pollution and reduces the clutter of "Caused by" exceptions in the
* stacktrace.
* <p>
* The use of this technique may be controversial, but exceedingly useful to
* library developers.
* <code>
* public int propagateExample { // note that there is no throws clause
* try {
* return invocation(); // throws IOException
* } catch (Exception e) {
* return ExceptionUtils.rethrow(e); // propagates a checked exception
* }
* }
* </code>
* <p>
* This is an alternative to the more conservative approach of wrapping the
* checked exception in a RuntimeException:
* <code>
* public int wrapExample { // note that there is no throws clause
* try {
* return invocation(); // throws IOException
* } catch (Error e) {
* throw e;
* } catch (RuntimeException e) {
* throw e; // wraps a checked exception
* } catch (Exception e) {
* throw new UndeclaredThrowableException(e); // wraps a checked exception
* }
* }
* </code>
* <p>
* One downside to using this approach is that the java compiler will not
* allow invoking code to specify a checked exception in a catch clause
* unless there is some code path within the try block that has invoked a
* method declared with that checked exception. If the invoking site wishes
* to catch the shaded checked exception, it must either invoke the shaded
* code through a method re-declaring the desired checked exception, or
* catch Exception and use the instanceof operator. Either of these
* techniques are required when interacting with non-java jvm code such as
* Jython, Scala, or Groovy, since these languages do not consider any
* exceptions as checked.
*
* @param throwable
* The throwable to rethrow.
* @param <R> The type of the returned value.
* @return Never actually returned, this generic type matches any type
* which the calling site requires. "Returning" the results of this
* method, as done in the propagateExample above, will satisfy the
* java compiler requirement that all code paths return a value.
* @since 3.5
* @see #wrapAndThrow(Throwable)
*/
public static <R> R rethrow(final Throwable throwable) {
// claim that the typeErasure invocation throws a RuntimeException
return ExceptionUtils.<R, RuntimeException> typeErasure(throwable);
}
/**
* Claim a Throwable is another Exception type using type erasure. This
* hides a checked exception from the java compiler, allowing a checked
* exception to be thrown without having the exception in the method's throw
* clause.
*/
@SuppressWarnings("unchecked")
private static <R, T extends Throwable> R typeErasure(final Throwable throwable) throws T {
throw (T) throwable;
}
/**
* Throw a checked exception without adding the exception to the throws
* clause of the calling method. For checked exceptions, this method throws
* an UndeclaredThrowableException wrapping the checked exception. For
* Errors and RuntimeExceptions, the original exception is rethrown.
* <p>
* The downside to using this approach is that invoking code which needs to
* handle specific checked exceptions must sniff up the exception chain to
* determine if the caught exception was caused by the checked exception.
*
* @param throwable
* The throwable to rethrow.
* @param <R> The type of the returned value.
* @return Never actually returned, this generic type matches any type
* which the calling site requires. "Returning" the results of this
* method will satisfy the java compiler requirement that all code
* paths return a value.
* @since 3.5
* @see #rethrow(Throwable)
* @see #hasCause(Throwable, Class)
*/
public static <R> R wrapAndThrow(final Throwable throwable) {
if (throwable instanceof RuntimeException) {
throw (RuntimeException) throwable;
}
if (throwable instanceof Error) {
throw (Error) throwable;
}
throw new UndeclaredThrowableException(throwable);
}
/**
* Does the throwable's causal chain have an immediate or wrapped exception
* of the given type?
*
* @param chain
* The root of a Throwable causal chain.
* @param type
* The exception type to test.
* @return true, if chain is an instance of type or is an
* UndeclaredThrowableException wrapping a cause.
* @since 3.5
* @see #wrapAndThrow(Throwable)
*/
public static boolean hasCause(Throwable chain,
final Class<? extends Throwable> type) {
if (chain instanceof UndeclaredThrowableException) {
chain = chain.getCause();
}
return type.isInstance(chain);
}
}

View File

@ -1,254 +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 com.qihoo360.replugin.ext.lang3.math;
import com.qihoo360.replugin.ext.lang3.Validate;
/**
* <p>Provides IEEE-754r variants of NumberUtils methods. </p>
*
* <p>See: <a href="http://en.wikipedia.org/wiki/IEEE_754r">http://en.wikipedia.org/wiki/IEEE_754r</a></p>
*
* @since 2.4
*/
public class IEEE754rUtils {
/**
* <p>Returns the minimum value in an array.</p>
*
* @param array an array, must not be null or empty
* @return the minimum value in the array
* @throws IllegalArgumentException if <code>array</code> is <code>null</code>
* @throws IllegalArgumentException if <code>array</code> is empty
* @since 3.4 Changed signature from min(double[]) to min(double...)
*/
public static double min(final double... array) {
Validate.isTrue(array != null, "The Array must not be null");
Validate.isTrue(array.length != 0, "Array cannot be empty.");
// Finds and returns min
double min = array[0];
for (int i = 1; i < array.length; i++) {
min = min(array[i], min);
}
return min;
}
/**
* <p>Returns the minimum value in an array.</p>
*
* @param array an array, must not be null or empty
* @return the minimum value in the array
* @throws IllegalArgumentException if <code>array</code> is <code>null</code>
* @throws IllegalArgumentException if <code>array</code> is empty
* @since 3.4 Changed signature from min(float[]) to min(float...)
*/
public static float min(final float... array) {
Validate.isTrue(array != null, "The Array must not be null");
Validate.isTrue(array.length != 0, "Array cannot be empty.");
// Finds and returns min
float min = array[0];
for (int i = 1; i < array.length; i++) {
min = min(array[i], min);
}
return min;
}
/**
* <p>Gets the minimum of three <code>double</code> values.</p>
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r. </p>
*
* @param a value 1
* @param b value 2
* @param c value 3
* @return the smallest of the values
*/
public static double min(final double a, final double b, final double c) {
return min(min(a, b), c);
}
/**
* <p>Gets the minimum of two <code>double</code> values.</p>
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r. </p>
*
* @param a value 1
* @param b value 2
* @return the smallest of the values
*/
public static double min(final double a, final double b) {
if(Double.isNaN(a)) {
return b;
} else
if(Double.isNaN(b)) {
return a;
} else {
return Math.min(a, b);
}
}
/**
* <p>Gets the minimum of three <code>float</code> values.</p>
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r. </p>
*
* @param a value 1
* @param b value 2
* @param c value 3
* @return the smallest of the values
*/
public static float min(final float a, final float b, final float c) {
return min(min(a, b), c);
}
/**
* <p>Gets the minimum of two <code>float</code> values.</p>
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r. </p>
*
* @param a value 1
* @param b value 2
* @return the smallest of the values
*/
public static float min(final float a, final float b) {
if(Float.isNaN(a)) {
return b;
} else
if(Float.isNaN(b)) {
return a;
} else {
return Math.min(a, b);
}
}
/**
* <p>Returns the maximum value in an array.</p>
*
* @param array an array, must not be null or empty
* @return the minimum value in the array
* @throws IllegalArgumentException if <code>array</code> is <code>null</code>
* @throws IllegalArgumentException if <code>array</code> is empty
* @since 3.4 Changed signature from max(double[]) to max(double...)
*/
public static double max(final double... array) {
Validate.isTrue(array != null, "The Array must not be null");
Validate.isTrue(array.length != 0, "Array cannot be empty.");
// Finds and returns max
double max = array[0];
for (int j = 1; j < array.length; j++) {
max = max(array[j], max);
}
return max;
}
/**
* <p>Returns the maximum value in an array.</p>
*
* @param array an array, must not be null or empty
* @return the minimum value in the array
* @throws IllegalArgumentException if <code>array</code> is <code>null</code>
* @throws IllegalArgumentException if <code>array</code> is empty
* @since 3.4 Changed signature from max(float[]) to max(float...)
*/
public static float max(final float... array) {
Validate.isTrue(array != null, "The Array must not be null");
Validate.isTrue(array.length != 0, "Array cannot be empty.");
// Finds and returns max
float max = array[0];
for (int j = 1; j < array.length; j++) {
max = max(array[j], max);
}
return max;
}
/**
* <p>Gets the maximum of three <code>double</code> values.</p>
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r. </p>
*
* @param a value 1
* @param b value 2
* @param c value 3
* @return the largest of the values
*/
public static double max(final double a, final double b, final double c) {
return max(max(a, b), c);
}
/**
* <p>Gets the maximum of two <code>double</code> values.</p>
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r. </p>
*
* @param a value 1
* @param b value 2
* @return the largest of the values
*/
public static double max(final double a, final double b) {
if(Double.isNaN(a)) {
return b;
} else
if(Double.isNaN(b)) {
return a;
} else {
return Math.max(a, b);
}
}
/**
* <p>Gets the maximum of three <code>float</code> values.</p>
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r. </p>
*
* @param a value 1
* @param b value 2
* @param c value 3
* @return the largest of the values
*/
public static float max(final float a, final float b, final float c) {
return max(max(a, b), c);
}
/**
* <p>Gets the maximum of two <code>float</code> values.</p>
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r. </p>
*
* @param a value 1
* @param b value 2
* @return the largest of the values
*/
public static float max(final float a, final float b) {
if(Float.isNaN(a)) {
return b;
} else
if(Float.isNaN(b)) {
return a;
} else {
return Math.max(a, b);
}
}
}

View File

@ -1,53 +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 com.qihoo360.replugin.ext.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.
*
* @param <T> the type to set and get
* @since 2.1
*/
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);
}

View File

@ -1,382 +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 com.qihoo360.replugin.ext.lang3.mutable;
import com.qihoo360.replugin.ext.lang3.math.NumberUtils;
/**
* 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
*/
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(final 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(final 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(final 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
*/
@Override
public Integer getValue() {
return Integer.valueOf(this.value);
}
/**
* Sets the value.
*
* @param value the value to set
*/
public void setValue(final 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
*/
@Override
public void setValue(final Number value) {
this.value = value.intValue();
}
//-----------------------------------------------------------------------
/**
* Increments the value.
*
* @since Commons Lang 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public int getAndIncrement() {
final int last = value;
value++;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public int incrementAndGet() {
value++;
return value;
}
/**
* Decrements the value.
*
* @since Commons Lang 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public int getAndDecrement() {
final int last = value;
value--;
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public int decrementAndGet() {
value--;
return 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(final 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(final 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(final 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(final Number operand) {
this.value -= operand.intValue();
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately after the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @return the value associated with this instance after adding the operand
* @since 3.5
*/
public int addAndGet(final int operand) {
this.value += operand;
return value;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately after the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @throws NullPointerException if {@code operand} is null
* @return the value associated with this instance after adding the operand
* @since 3.5
*/
public int addAndGet(final Number operand) {
this.value += operand.intValue();
return value;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @return the value associated with this instance immediately before the operand was added
* @since 3.5
*/
public int getAndAdd(final int operand) {
final int last = value;
this.value += operand;
return last;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @throws NullPointerException if {@code operand} is null
* @return the value associated with this instance immediately before the operand was added
* @since 3.5
*/
public int getAndAdd(final Number operand) {
final int last = value;
this.value += operand.intValue();
return last;
}
//-----------------------------------------------------------------------
// 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(final 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
*/
@Override
public int compareTo(final MutableInt other) {
return NumberUtils.compare(this.value, other.value);
}
//-----------------------------------------------------------------------
/**
* Returns the String value of this mutable.
*
* @return the mutable value as a string
*/
@Override
public String toString() {
return String.valueOf(value);
}
}

View File

@ -1,136 +0,0 @@
/*
* Copyright (c) 2016, Liu Dong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.qihoo360.replugin.ext.lang3.mutable;
import java.io.Serializable;
/**
* A mutable <code>Object</code> wrapper.
*
* @param <T> the type to set and get
* @since 2.1
*/
public class MutableObject<T> implements Mutable<T>, Serializable {
/**
* Required for serialization support.
*
* @see Serializable
*/
private static final long serialVersionUID = 86241875189L;
/** The mutable value. */
private T value;
/**
* Constructs a new MutableObject with the default value of <code>null</code>.
*/
public MutableObject() {
super();
}
/**
* Constructs a new MutableObject with the specified value.
*
* @param value the initial value to store
*/
public MutableObject(final T value) {
super();
this.value = value;
}
//-----------------------------------------------------------------------
/**
* Gets the value.
*
* @return the value, may be null
*/
@Override
public T getValue() {
return this.value;
}
/**
* Sets the value.
*
* @param value the value to set
*/
@Override
public void setValue(final T value) {
this.value = value;
}
//-----------------------------------------------------------------------
/**
* <p>
* Compares this object against the specified object. The result is <code>true</code> if and only if the argument
* is not <code>null</code> and is a <code>MutableObject</code> object that contains the same <code>T</code>
* value as this object.
* </p>
*
* @param obj the object to compare with, <code>null</code> returns <code>false</code>
* @return <code>true</code> if the objects are the same;
* <code>true</code> if the objects have equivalent <code>value</code> fields;
* <code>false</code> otherwise.
*/
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (this.getClass() == obj.getClass()) {
final MutableObject<?> that = (MutableObject<?>) obj;
return this.value.equals(that.value);
}
return false;
}
/**
* Returns the value's hash code or <code>0</code> if the value is <code>null</code>.
*
* @return the value's hash code or <code>0</code> if the value is <code>null</code>.
*/
@Override
public int hashCode() {
return value == null ? 0 : value.hashCode();
}
//-----------------------------------------------------------------------
/**
* Returns the String value of this mutable.
*
* @return the mutable value as a string
*/
@Override
public String toString() {
return value == null ? "null" : value.toString();
}
}

View File

@ -1,311 +0,0 @@
/*
* Copyright (c) 2016, Liu Dong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.qihoo360.replugin.ext.lang3.reflect;
import com.qihoo360.replugin.ext.lang3.ArrayUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import com.qihoo360.replugin.ext.lang3.ClassUtils;
import com.qihoo360.replugin.ext.lang3.Validate;
/**
* <p> Utility reflection methods focused on constructors, modeled after
* {@link MethodUtils}. </p>
*
* <h3>Known Limitations</h3> <h4>Accessing Public Constructors In A Default
* Access Superclass</h4> <p>There is an issue when invoking {@code public} constructors
* contained in a default access superclass. Reflection correctly locates these
* constructors and assigns them as {@code public}. However, an
* {@link IllegalAccessException} is thrown if the constructor is
* invoked.</p>
*
* <p>{@link ConstructorUtils} contains a workaround for this situation: it
* will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this constructor. 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 a warning will be logged and the method may fail.</p>
*
* @since 2.5
*/
public class ConstructorUtils {
/**
* <p>ConstructorUtils instances should NOT be constructed in standard
* programming. Instead, the class should be used as
* {@code ConstructorUtils.invokeConstructor(cls, args)}.</p>
*
* <p>This constructor is {@code public} to permit tools that require a JavaBean
* instance to operate.</p>
*/
public ConstructorUtils() {
super();
}
/**
* <p>Returns a new instance of the specified class inferring the right constructor
* from the types of the arguments.</p>
*
* <p>This locates and calls a constructor.
* The constructor signature must match the argument types by assignment compatibility.</p>
*
* @param <T> the type to be constructed
* @param cls the class to be constructed, not {@code null}
* @param args the array of arguments, {@code null} treated as empty
* @return new instance of {@code cls}, not {@code null}
*
* @throws NullPointerException if {@code cls} is {@code null}
* @throws NoSuchMethodException if a matching constructor cannot be found
* @throws IllegalAccessException if invocation is not permitted by security
* @throws InvocationTargetException if an error occurs on invocation
* @throws InstantiationException if an error occurs on instantiation
* @see #invokeConstructor(Class, Object[], Class[])
*/
public static <T> T invokeConstructor(final Class<T> cls, Object... args)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
InstantiationException {
args = ArrayUtils.nullToEmpty(args);
final Class<?> parameterTypes[] = ClassUtils.toClass(args);
return invokeConstructor(cls, args, parameterTypes);
}
/**
* <p>Returns a new instance of the specified class choosing the right constructor
* from the list of parameter types.</p>
*
* <p>This locates and calls a constructor.
* The constructor signature must match the parameter types by assignment compatibility.</p>
*
* @param <T> the type to be constructed
* @param cls the class to be constructed, not {@code null}
* @param args the array of arguments, {@code null} treated as empty
* @param parameterTypes the array of parameter types, {@code null} treated as empty
* @return new instance of {@code cls}, not {@code null}
*
* @throws NullPointerException if {@code cls} is {@code null}
* @throws NoSuchMethodException if a matching constructor cannot be found
* @throws IllegalAccessException if invocation is not permitted by security
* @throws InvocationTargetException if an error occurs on invocation
* @throws InstantiationException if an error occurs on instantiation
* @see Constructor#newInstance
*/
public static <T> T invokeConstructor(final Class<T> cls, Object[] args, Class<?>[] parameterTypes)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
InstantiationException {
args = ArrayUtils.nullToEmpty(args);
parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
final Constructor<T> ctor = getMatchingAccessibleConstructor(cls, parameterTypes);
if (ctor == null) {
throw new NoSuchMethodException(
"No such accessible constructor on object: " + cls.getName());
}
if (ctor.isVarArgs()) {
final Class<?>[] methodParameterTypes = ctor.getParameterTypes();
args = MethodUtils.getVarArgs(args, methodParameterTypes);
}
return ctor.newInstance(args);
}
/**
* <p>Returns a new instance of the specified class inferring the right constructor
* from the types of the arguments.</p>
*
* <p>This locates and calls a constructor.
* The constructor signature must match the argument types exactly.</p>
*
* @param <T> the type to be constructed
* @param cls the class to be constructed, not {@code null}
* @param args the array of arguments, {@code null} treated as empty
* @return new instance of {@code cls}, not {@code null}
*
* @throws NullPointerException if {@code cls} is {@code null}
* @throws NoSuchMethodException if a matching constructor cannot be found
* @throws IllegalAccessException if invocation is not permitted by security
* @throws InvocationTargetException if an error occurs on invocation
* @throws InstantiationException if an error occurs on instantiation
* @see #invokeExactConstructor(Class, Object[], Class[])
*/
public static <T> T invokeExactConstructor(final Class<T> cls, Object... args)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
InstantiationException {
args = ArrayUtils.nullToEmpty(args);
final Class<?> parameterTypes[] = ClassUtils.toClass(args);
return invokeExactConstructor(cls, args, parameterTypes);
}
/**
* <p>Returns a new instance of the specified class choosing the right constructor
* from the list of parameter types.</p>
*
* <p>This locates and calls a constructor.
* The constructor signature must match the parameter types exactly.</p>
*
* @param <T> the type to be constructed
* @param cls the class to be constructed, not {@code null}
* @param args the array of arguments, {@code null} treated as empty
* @param parameterTypes the array of parameter types, {@code null} treated as empty
* @return new instance of <code>cls</code>, not {@code null}
*
* @throws NullPointerException if {@code cls} is {@code null}
* @throws NoSuchMethodException if a matching constructor cannot be found
* @throws IllegalAccessException if invocation is not permitted by security
* @throws InvocationTargetException if an error occurs on invocation
* @throws InstantiationException if an error occurs on instantiation
* @see Constructor#newInstance
*/
public static <T> T invokeExactConstructor(final Class<T> cls, Object[] args,
Class<?>[] parameterTypes) throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
args = ArrayUtils.nullToEmpty(args);
parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
final Constructor<T> ctor = getAccessibleConstructor(cls, parameterTypes);
if (ctor == null) {
throw new NoSuchMethodException(
"No such accessible constructor on object: "+ cls.getName());
}
return ctor.newInstance(args);
}
//-----------------------------------------------------------------------
/**
* <p>Finds a constructor given a class and signature, checking accessibility.</p>
*
* <p>This finds the constructor and ensures that it is accessible.
* The constructor signature must match the parameter types exactly.</p>
*
* @param <T> the constructor type
* @param cls the class to find a constructor for, not {@code null}
* @param parameterTypes the array of parameter types, {@code null} treated as empty
* @return the constructor, {@code null} if no matching accessible constructor found
* @see Class#getConstructor
* @see #getAccessibleConstructor(Constructor)
* @throws NullPointerException if {@code cls} is {@code null}
*/
public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls,
final Class<?>... parameterTypes) {
Validate.notNull(cls, "class cannot be null");
try {
return getAccessibleConstructor(cls.getConstructor(parameterTypes));
} catch (final NoSuchMethodException e) {
return null;
}
}
/**
* <p>Checks if the specified constructor is accessible.</p>
*
* <p>This simply ensures that the constructor is accessible.</p>
*
* @param <T> the constructor type
* @param ctor the prototype constructor object, not {@code null}
* @return the constructor, {@code null} if no matching accessible constructor found
* @see SecurityManager
* @throws NullPointerException if {@code ctor} is {@code null}
*/
public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
Validate.notNull(ctor, "constructor cannot be null");
return MemberUtils.isAccessible(ctor)
&& isAccessible(ctor.getDeclaringClass()) ? ctor : null;
}
/**
* <p>Finds an accessible constructor with compatible parameters.</p>
*
* <p>This checks all the constructor and finds one with compatible parameters
* This requires that every parameter is assignable from the given parameter types.
* This is a more flexible search than the normal exact matching algorithm.</p>
*
* <p>First it checks if there is a constructor matching the exact signature.
* If not then all the constructors of the class are checked to see if their
* signatures are assignment-compatible with the parameter types.
* The first assignment-compatible matching constructor is returned.</p>
*
* @param <T> the constructor type
* @param cls the class to find a constructor for, not {@code null}
* @param parameterTypes find method with compatible parameters
* @return the constructor, null if no matching accessible constructor found
* @throws NullPointerException if {@code cls} is {@code null}
*/
public static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> cls,
final Class<?>... parameterTypes) {
Validate.notNull(cls, "class cannot be null");
// see if we can find the constructor directly
// most of the time this works and it's much faster
try {
final Constructor<T> ctor = cls.getConstructor(parameterTypes);
MemberUtils.setAccessibleWorkaround(ctor);
return ctor;
} catch (final NoSuchMethodException e) { // NOPMD - Swallow
}
Constructor<T> result = null;
/*
* (1) Class.getConstructors() is documented to return Constructor<T> so as
* long as the array is not subsequently modified, everything's fine.
*/
final Constructor<?>[] ctors = cls.getConstructors();
// return best match:
for (Constructor<?> ctor : ctors) {
// compare parameters
if (MemberUtils.isMatchingConstructor(ctor, parameterTypes)) {
// get accessible version of constructor
ctor = getAccessibleConstructor(ctor);
if (ctor != null) {
MemberUtils.setAccessibleWorkaround(ctor);
if (result == null || MemberUtils.compareConstructorFit(ctor, result, parameterTypes) < 0) {
// temporary variable for annotation, see comment above (1)
@SuppressWarnings("unchecked")
final
Constructor<T> constructor = (Constructor<T>)ctor;
result = constructor;
}
}
}
}
return result;
}
/**
* Learn whether the specified class is generally accessible, i.e. is
* declared in an entirely {@code public} manner.
* @param type to check
* @return {@code true} if {@code type} and any enclosing classes are
* {@code public}.
*/
private static boolean isAccessible(final Class<?> type) {
Class<?> cls = type;
while (cls != null) {
if (!Modifier.isPublic(cls.getModifiers())) {
return false;
}
cls = cls.getEnclosingClass();
}
return true;
}
}

View File

@ -1,839 +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 com.qihoo360.replugin.ext.lang3.reflect;
import com.qihoo360.replugin.ext.lang3.StringUtils;
import com.qihoo360.replugin.ext.lang3.Validate;
import com.qihoo360.replugin.ext.lang3.ClassUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
/**
* Utilities for working with {@link Field}s by reflection. Adapted and refactored from the dormant [reflect] Commons
* sandbox component.
* <p>
* The ability is provided to break the scoping restrictions coded by the programmer. This can allow fields to be
* changed that shouldn't be. This facility should be used with care.
*
* @since 2.5
*/
public class FieldUtils {
/**
* {@link FieldUtils} instances should NOT be constructed in standard programming.
* <p>
* This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
* </p>
*/
public FieldUtils() {
super();
}
/**
* Gets an accessible {@link Field} by name respecting scope. Superclasses/interfaces will be considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @return the Field object
* @throws IllegalArgumentException
* if the class is {@code null}, or the field name is blank or empty
*/
public static Field getField(final Class<?> cls, final String fieldName) {
final Field field = getField(cls, fieldName, false);
MemberUtils.setAccessibleWorkaround(field);
return field;
}
/**
* Gets an accessible {@link Field} by name, breaking scope if requested. Superclasses/interfaces will be
* considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @return the Field object
* @throws IllegalArgumentException
* if the class is {@code null}, or the field name is blank or empty or is matched at multiple places
* in the inheritance hierarchy
*/
public static Field getField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
Validate.isTrue(cls != null, "The class must not be null");
Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
// FIXME is this workaround still needed? lang requires Java 6
// Sun Java 1.3 has a bugged implementation of getField hence we write the
// code ourselves
// getField() will return the Field object with the declaring class
// set correctly to the class that declares the field. Thus requesting the
// field on a subclass will return the field from the superclass.
//
// priority order for lookup:
// searchclass private/protected/package/public
// superclass protected/package/public
// private/different package blocks access to further superclasses
// implementedinterface public
// check up the superclass hierarchy
for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
try {
final Field field = acls.getDeclaredField(fieldName);
// getDeclaredField checks for non-public scopes as well
// and it returns accurate results
if (!Modifier.isPublic(field.getModifiers())) {
if (forceAccess) {
field.setAccessible(true);
} else {
continue;
}
}
return field;
} catch (final NoSuchFieldException ex) { // NOPMD
// ignore
}
}
// check the public interface case. This must be manually searched for
// incase there is a public supersuperclass field hidden by a private/package
// superclass field.
Field match = null;
for (final Class<?> class1 : ClassUtils.getAllInterfaces(cls)) {
try {
final Field test = class1.getField(fieldName);
Validate.isTrue(match == null, "Reference to field %s is ambiguous relative to %s"
+ "; a matching field exists on two or more implemented interfaces.", fieldName, cls);
match = test;
} catch (final NoSuchFieldException ex) { // NOPMD
// ignore
}
}
return match;
}
/**
* Gets an accessible {@link Field} by name respecting scope. Only the specified class will be considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @return the Field object
* @throws IllegalArgumentException
* if the class is {@code null}, or the field name is blank or empty
*/
public static Field getDeclaredField(final Class<?> cls, final String fieldName) {
return getDeclaredField(cls, fieldName, false);
}
/**
* Gets an accessible {@link Field} by name, breaking scope if requested. Only the specified class will be
* considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @return the Field object
* @throws IllegalArgumentException
* if the class is {@code null}, or the field name is blank or empty
*/
public static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
Validate.isTrue(cls != null, "The class must not be null");
Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
try {
// only consider the specified class by using getDeclaredField()
final Field field = cls.getDeclaredField(fieldName);
if (!MemberUtils.isAccessible(field)) {
if (forceAccess) {
field.setAccessible(true);
} else {
return null;
}
}
return field;
} catch (final NoSuchFieldException e) { // NOPMD
// ignore
}
return null;
}
/**
* Gets all fields of the given class and its parents (if any).
*
* @param cls
* the {@link Class} to query
* @return an array of Fields (possibly empty).
* @throws IllegalArgumentException
* if the class is {@code null}
* @since 3.2
*/
public static Field[] getAllFields(final Class<?> cls) {
final List<Field> allFieldsList = getAllFieldsList(cls);
return allFieldsList.toArray(new Field[allFieldsList.size()]);
}
/**
* Gets all fields of the given class and its parents (if any).
*
* @param cls
* the {@link Class} to query
* @return an array of Fields (possibly empty).
* @throws IllegalArgumentException
* if the class is {@code null}
* @since 3.2
*/
public static List<Field> getAllFieldsList(final Class<?> cls) {
Validate.isTrue(cls != null, "The class must not be null");
final List<Field> allFields = new ArrayList<>();
Class<?> currentClass = cls;
while (currentClass != null) {
final Field[] declaredFields = currentClass.getDeclaredFields();
for (final Field field : declaredFields) {
allFields.add(field);
}
currentClass = currentClass.getSuperclass();
}
return allFields;
}
/**
* Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
* @param cls
* the {@link Class} to query
* @param annotationCls
* the {@link Annotation} that must be present on a field to be matched
* @return an array of Fields (possibly empty).
* @throws IllegalArgumentException
* if the class or annotation are {@code null}
* @since 3.4
*/
public static Field[] getFieldsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
final List<Field> annotatedFieldsList = getFieldsListWithAnnotation(cls, annotationCls);
return annotatedFieldsList.toArray(new Field[annotatedFieldsList.size()]);
}
/**
* Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
* @param cls
* the {@link Class} to query
* @param annotationCls
* the {@link Annotation} that must be present on a field to be matched
* @return a list of Fields (possibly empty).
* @throws IllegalArgumentException
* if the class or annotation are {@code null}
* @since 3.4
*/
public static List<Field> getFieldsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
Validate.isTrue(annotationCls != null, "The annotation class must not be null");
final List<Field> allFields = getAllFieldsList(cls);
final List<Field> annotatedFields = new ArrayList<>();
for (final Field field : allFields) {
if (field.getAnnotation(annotationCls) != null) {
annotatedFields.add(field);
}
}
return annotatedFields;
}
/**
* Reads an accessible {@code static} {@link Field}.
*
* @param field
* to read
* @return the field value
* @throws IllegalArgumentException
* if the field is {@code null}, or not {@code static}
* @throws IllegalAccessException
* if the field is not accessible
*/
public static Object readStaticField(final Field field) throws IllegalAccessException {
return readStaticField(field, false);
}
/**
* Reads a static {@link Field}.
*
* @param field
* to read
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
* @return the field value
* @throws IllegalArgumentException
* if the field is {@code null} or not {@code static}
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException {
Validate.isTrue(field != null, "The field must not be null");
Validate.isTrue(Modifier.isStatic(field.getModifiers()), "The field '%s' is not static", field.getName());
return readField(field, (Object) null, forceAccess);
}
/**
* Reads the named {@code public static} {@link Field}. Superclasses will be considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @return the value of the field
* @throws IllegalArgumentException
* if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
* not be found
* @throws IllegalAccessException
* if the field is not accessible
*/
public static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
return readStaticField(cls, fieldName, false);
}
/**
* Reads the named {@code static} {@link Field}. Superclasses will be considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @return the Field object
* @throws IllegalArgumentException
* if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
* not be found
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static Object readStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
final Field field = getField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate field '%s' on %s", fieldName, cls);
// already forced access above, don't repeat it here:
return readStaticField(field, false);
}
/**
* Gets the value of a {@code static} {@link Field} by name. The field must be {@code public}. Only the specified
* class will be considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @return the value of the field
* @throws IllegalArgumentException
* if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
* not be found
* @throws IllegalAccessException
* if the field is not accessible
*/
public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
return readDeclaredStaticField(cls, fieldName, false);
}
/**
* Gets the value of a {@code static} {@link Field} by name. Only the specified class will be considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @return the Field object
* @throws IllegalArgumentException
* if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
* not be found
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
final Field field = getDeclaredField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
// already forced access above, don't repeat it here:
return readStaticField(field, false);
}
/**
* Reads an accessible {@link Field}.
*
* @param field
* the field to use
* @param target
* the object to call on, may be {@code null} for {@code static} fields
* @return the field value
* @throws IllegalArgumentException
* if the field is {@code null}
* @throws IllegalAccessException
* if the field is not accessible
*/
public static Object readField(final Field field, final Object target) throws IllegalAccessException {
return readField(field, target, false);
}
/**
* Reads a {@link Field}.
*
* @param field
* the field to use
* @param target
* the object to call on, may be {@code null} for {@code static} fields
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
* @return the field value
* @throws IllegalArgumentException
* if the field is {@code null}
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static Object readField(final Field field, final Object target, final boolean forceAccess) throws IllegalAccessException {
Validate.isTrue(field != null, "The field must not be null");
if (forceAccess && !field.isAccessible()) {
field.setAccessible(true);
} else {
MemberUtils.setAccessibleWorkaround(field);
}
return field.get(target);
}
/**
* Reads the named {@code public} {@link Field}. Superclasses will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @return the value of the field
* @throws IllegalArgumentException
* if the class is {@code null}, or the field name is blank or empty or could not be found
* @throws IllegalAccessException
* if the named field is not {@code public}
*/
public static Object readField(final Object target, final String fieldName) throws IllegalAccessException {
return readField(target, fieldName, false);
}
/**
* Reads the named {@link Field}. Superclasses will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @return the field value
* @throws IllegalArgumentException
* if {@code target} is {@code null}, or the field name is blank or empty or could not be found
* @throws IllegalAccessException
* if the named field is not made accessible
*/
public static Object readField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
Validate.isTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
// already forced access above, don't repeat it here:
return readField(field, target, false);
}
/**
* Reads the named {@code public} {@link Field}. Only the class of the specified object will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @return the value of the field
* @throws IllegalArgumentException
* if {@code target} is {@code null}, or the field name is blank or empty or could not be found
* @throws IllegalAccessException
* if the named field is not {@code public}
*/
public static Object readDeclaredField(final Object target, final String fieldName) throws IllegalAccessException {
return readDeclaredField(target, fieldName, false);
}
/**
* Gets a {@link Field} value by name. Only the class of the specified object will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match public fields.
* @return the Field object
* @throws IllegalArgumentException
* if {@code target} is {@code null}, or the field name is blank or empty or could not be found
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static Object readDeclaredField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
Validate.isTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getDeclaredField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls, fieldName);
// already forced access above, don't repeat it here:
return readField(field, target, false);
}
/**
* Writes a {@code public static} {@link Field}.
*
* @param field
* to write
* @param value
* to set
* @throws IllegalArgumentException
* if the field is {@code null} or not {@code static}, or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not {@code public} or is {@code final}
*/
public static void writeStaticField(final Field field, final Object value) throws IllegalAccessException {
writeStaticField(field, value, false);
}
/**
* Writes a static {@link Field}.
*
* @param field
* to write
* @param value
* to set
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @throws IllegalArgumentException
* if the field is {@code null} or not {@code static}, or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible or is {@code final}
*/
public static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException {
Validate.isTrue(field != null, "The field must not be null");
Validate.isTrue(Modifier.isStatic(field.getModifiers()), "The field %s.%s is not static", field.getDeclaringClass().getName(),
field.getName());
writeField(field, (Object) null, value, forceAccess);
}
/**
* Writes a named {@code public static} {@link Field}. Superclasses will be considered.
*
* @param cls
* {@link Class} on which the field is to be found
* @param fieldName
* to write
* @param value
* to set
* @throws IllegalArgumentException
* if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
* not {@code static}, or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not {@code public} or is {@code final}
*/
public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
writeStaticField(cls, fieldName, value, false);
}
/**
* Writes a named {@code static} {@link Field}. Superclasses will be considered.
*
* @param cls
* {@link Class} on which the field is to be found
* @param fieldName
* to write
* @param value
* to set
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @throws IllegalArgumentException
* if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
* not {@code static}, or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible or is {@code final}
*/
public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
throws IllegalAccessException {
final Field field = getField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
// already forced access above, don't repeat it here:
writeStaticField(field, value, false);
}
/**
* Writes a named {@code public static} {@link Field}. Only the specified class will be considered.
*
* @param cls
* {@link Class} on which the field is to be found
* @param fieldName
* to write
* @param value
* to set
* @throws IllegalArgumentException
* if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
* not {@code static}, or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not {@code public} or is {@code final}
*/
public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
writeDeclaredStaticField(cls, fieldName, value, false);
}
/**
* Writes a named {@code static} {@link Field}. Only the specified class will be considered.
*
* @param cls
* {@link Class} on which the field is to be found
* @param fieldName
* to write
* @param value
* to set
* @param forceAccess
* whether to break scope restrictions using the {@code AccessibleObject#setAccessible(boolean)} method.
* {@code false} will only match {@code public} fields.
* @throws IllegalArgumentException
* if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
* not {@code static}, or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible or is {@code final}
*/
public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
throws IllegalAccessException {
final Field field = getDeclaredField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
// already forced access above, don't repeat it here:
writeField(field, (Object) null, value, false);
}
/**
* Writes an accessible {@link Field}.
*
* @param field
* to write
* @param target
* the object to call on, may be {@code null} for {@code static} fields
* @param value
* to set
* @throws IllegalAccessException
* if the field or target is {@code null}, the field is not accessible or is {@code final}, or
* {@code value} is not assignable
*/
public static void writeField(final Field field, final Object target, final Object value) throws IllegalAccessException {
writeField(field, target, value, false);
}
/**
* Writes a {@link Field}.
*
* @param field
* to write
* @param target
* the object to call on, may be {@code null} for {@code static} fields
* @param value
* to set
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @throws IllegalArgumentException
* if the field is {@code null} or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible or is {@code final}
*/
public static void writeField(final Field field, final Object target, final Object value, final boolean forceAccess)
throws IllegalAccessException {
Validate.isTrue(field != null, "The field must not be null");
if (forceAccess && !field.isAccessible()) {
field.setAccessible(true);
} else {
MemberUtils.setAccessibleWorkaround(field);
}
field.set(target, value);
}
/**
* Removes the final modifier from a {@link Field}.
*
* @param field
* to remove the final modifier
* @throws IllegalArgumentException
* if the field is {@code null}
* @since 3.2
*/
public static void removeFinalModifier(final Field field) {
removeFinalModifier(field, true);
}
/**
* Removes the final modifier from a {@link Field}.
*
* @param field
* to remove the final modifier
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @throws IllegalArgumentException
* if the field is {@code null}
* @since 3.3
*/
public static void removeFinalModifier(final Field field, final boolean forceAccess) {
Validate.isTrue(field != null, "The field must not be null");
try {
if (Modifier.isFinal(field.getModifiers())) {
// Do all JREs implement Field with a private ivar called "modifiers"?
final Field modifiersField = Field.class.getDeclaredField("modifiers");
final boolean doForceAccess = forceAccess && !modifiersField.isAccessible();
if (doForceAccess) {
modifiersField.setAccessible(true);
}
try {
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
} finally {
if (doForceAccess) {
modifiersField.setAccessible(false);
}
}
}
} catch (final NoSuchFieldException ignored) {
// The field class contains always a modifiers field
} catch (final IllegalAccessException ignored) {
// The modifiers field is made accessible
}
}
/**
* Writes a {@code public} {@link Field}. Superclasses will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param value
* to set
* @throws IllegalArgumentException
* if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
* {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not accessible
*/
public static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
writeField(target, fieldName, value, false);
}
/**
* Writes a {@link Field}. Superclasses will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param value
* to set
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @throws IllegalArgumentException
* if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
* {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
throws IllegalAccessException {
Validate.isTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
// already forced access above, don't repeat it here:
writeField(field, target, value, false);
}
/**
* Writes a {@code public} {@link Field}. Only the specified class will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param value
* to set
* @throws IllegalArgumentException
* if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
* {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
writeDeclaredField(target, fieldName, value, false);
}
/**
* Writes a {@code public} {@link Field}. Only the specified class will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param value
* to set
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @throws IllegalArgumentException
* if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
* {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static void writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
throws IllegalAccessException {
Validate.isTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getDeclaredField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
// already forced access above, don't repeat it here:
writeField(field, target, value, false);
}
}

View File

@ -1,320 +0,0 @@
/*
* Copyright (c) 2016, Liu Dong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.qihoo360.replugin.ext.lang3.reflect;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import com.qihoo360.replugin.ext.lang3.ClassUtils;
/**
* Contains common code for working with {@link Method Methods}/{@link Constructor Constructors},
* extracted and refactored from {@link MethodUtils} when it was imported from Commons BeanUtils.
*
* @since 2.5
*/
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 {@code public} class has a default access superclass with {@code 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 {@code public}.
* Calling {@code 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
* @return a boolean indicating whether the accessibility of the object was set to true.
*/
static boolean setAccessibleWorkaround(final AccessibleObject o) {
if (o == null || o.isAccessible()) {
return false;
}
final Member m = (Member) o;
if (!o.isAccessible() && Modifier.isPublic(m.getModifiers()) && isPackageAccess(m.getDeclaringClass().getModifiers())) {
try {
o.setAccessible(true);
return true;
} catch (final SecurityException e) { // NOPMD
// ignore in favor of subsequent IllegalAccessException
}
}
return false;
}
/**
* Returns whether a given set of modifiers implies package access.
* @param modifiers to test
* @return {@code true} unless {@code package}/{@code protected}/{@code private} modifier detected
*/
static boolean isPackageAccess(final int modifiers) {
return (modifiers & ACCESS_TEST) == 0;
}
/**
* Returns whether a {@link Member} is accessible.
* @param m Member to check
* @return {@code true} if <code>m</code> is accessible
*/
static boolean isAccessible(final Member m) {
return m != null && Modifier.isPublic(m.getModifiers()) && !m.isSynthetic();
}
/**
* Compares the relative fitness of two Constructors in terms of how well they
* match a 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" Constructor
* @param right the "right" Constructor
* @param actual the runtime parameter types to match against
* {@code left}/{@code right}
* @return int consistent with {@code compare} semantics
* @since 3.5
*/
static int compareConstructorFit(final Constructor<?> left, final Constructor<?> right, final Class<?>[] actual) {
return compareParameterTypes(Executable.of(left), Executable.of(right), actual);
}
/**
* Compares the relative fitness of two Methods in terms of how well they
* match a 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" Method
* @param right the "right" Method
* @param actual the runtime parameter types to match against
* {@code left}/{@code right}
* @return int consistent with {@code compare} semantics
* @since 3.5
*/
static int compareMethodFit(final Method left, final Method right, final Class<?>[] actual) {
return compareParameterTypes(Executable.of(left), Executable.of(right), actual);
}
/**
* Compares the relative fitness of two Executables in terms of how well they
* match a 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" Executable
* @param right the "right" Executable
* @param actual the runtime parameter types to match against
* {@code left}/{@code right}
* @return int consistent with {@code compare} semantics
*/
private static int compareParameterTypes(final Executable left, final Executable right, final Class<?>[] actual) {
final float leftCost = getTotalTransformationCost(actual, left);
final 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 executable The executable to calculate transformation costs for
* @return The total transformation cost
*/
private static float getTotalTransformationCost(final Class<?>[] srcArgs, final Executable executable) {
final Class<?>[] destArgs = executable.getParameterTypes();
final boolean isVarArgs = executable.isVarArgs();
// "source" and "destination" are the actual and declared args respectively.
float totalCost = 0.0f;
final long normalArgsLen = isVarArgs ? destArgs.length-1 : destArgs.length;
if (srcArgs.length < normalArgsLen) {
return Float.MAX_VALUE;
}
for (int i = 0; i < normalArgsLen; i++) {
totalCost += getObjectTransformationCost(srcArgs[i], destArgs[i]);
}
if (isVarArgs) {
// When isVarArgs is true, srcArgs and dstArgs may differ in length.
// There are two special cases to consider:
final boolean noVarArgsPassed = srcArgs.length < destArgs.length;
final boolean explicitArrayForVarags = srcArgs.length == destArgs.length && srcArgs[srcArgs.length-1].isArray();
final float varArgsCost = 0.001f;
final Class<?> destClass = destArgs[destArgs.length-1].getComponentType();
if (noVarArgsPassed) {
// When no varargs passed, the best match is the most generic matching type, not the most specific.
totalCost += getObjectTransformationCost(destClass, Object.class) + varArgsCost;
} else if (explicitArrayForVarags) {
final Class<?> sourceClass = srcArgs[srcArgs.length-1].getComponentType();
totalCost += getObjectTransformationCost(sourceClass, destClass) + varArgsCost;
} else {
// This is typical varargs case.
for (int i = destArgs.length-1; i < srcArgs.length; i++) {
final Class<?> srcClass = srcArgs[i];
totalCost += getObjectTransformationCost(srcClass, destClass) + varArgsCost;
}
}
}
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, final 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 traveled 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;
}
static boolean isMatchingMethod(final Method method, final Class<?>[] parameterTypes) {
return MemberUtils.isMatchingExecutable(Executable.of(method), parameterTypes);
}
static boolean isMatchingConstructor(final Constructor<?> method, final Class<?>[] parameterTypes) {
return MemberUtils.isMatchingExecutable(Executable.of(method), parameterTypes);
}
private static boolean isMatchingExecutable(final Executable method, final Class<?>[] parameterTypes) {
final Class<?>[] methodParameterTypes = method.getParameterTypes();
if (method.isVarArgs()) {
int i;
for (i = 0; i < methodParameterTypes.length - 1 && i < parameterTypes.length; i++) {
if (!ClassUtils.isAssignable(parameterTypes[i], methodParameterTypes[i], true)) {
return false;
}
}
final Class<?> varArgParameterType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
for (; i < parameterTypes.length; i++) {
if (!ClassUtils.isAssignable(parameterTypes[i], varArgParameterType, true)) {
return false;
}
}
return true;
}
return ClassUtils.isAssignable(parameterTypes, methodParameterTypes, true);
}
/**
* <p> A class providing a subset of the API of java.lang.reflect.Executable in Java 1.8,
* providing a common representation for function signatures for Constructors and Methods.</p>
*/
private static final class Executable {
private final Class<?>[] parameterTypes;
private final boolean isVarArgs;
private static Executable of(final Method method) {
return new Executable(method);
}
private static Executable of(final Constructor<?> constructor) {
return new Executable(constructor);
}
private Executable(final Method method) {
parameterTypes = method.getParameterTypes();
isVarArgs = method.isVarArgs();
}
private Executable(final Constructor<?> constructor) {
parameterTypes = constructor.getParameterTypes();
isVarArgs = constructor.isVarArgs();
}
public Class<?>[] getParameterTypes() {
return parameterTypes;
}
public boolean isVarArgs() {
return isVarArgs;
}
}
}

View File

@ -1,123 +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 com.qihoo360.replugin.ext.lang3.reflect;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import com.qihoo360.replugin.ext.lang3.Validate;
/**
* <p>Type literal comparable to {@code javax.enterprise.util.TypeLiteral},
* made generally available outside the JEE context. Allows the passing around of
* a "token" that represents a type in a typesafe manner, as opposed to
* passing the (non-parameterized) {@link Type} object itself. Consider:</p>
* <p>
* You might see such a typesafe API as:
* <pre>
* class Typesafe {
* &lt;T&gt; T obtain(Class&lt;T&gt; type, ...);
* }
* </pre>
* Consumed in the manner of:
* <pre>
* Foo foo = typesafe.obtain(Foo.class, ...);
* </pre>
* Yet, you run into problems when you want to do this with a parameterized type:
* <pre>
* List&lt;String&gt; listOfString = typesafe.obtain(List.class, ...); // could only give us a raw List
* </pre>
* {@code java.lang.reflect.Type} might provide some value:
* <pre>
* Type listOfStringType = ...; // firstly, how to obtain this? Doable, but not straightforward.
* List&lt;String&gt; listOfString = (List&lt;String&gt;) typesafe.obtain(listOfStringType, ...); // nongeneric Type would necessitate a cast
* </pre>
* The "type literal" concept was introduced to provide an alternative, i.e.:
* <pre>
* class Typesafe {
* &lt;T&gt; T obtain(TypeLiteral&lt;T&gt; type, ...);
* }
* </pre>
* Consuming code looks like:
* <pre>
* List&lt;String&gt; listOfString = typesafe.obtain(new TypeLiteral&lt;List&lt;String&gt;&gt;() {}, ...);
* </pre>
* <p>
* This has the effect of "jumping up" a level to tie a {@code java.lang.reflect.Type}
* to a type variable while simultaneously making it short work to obtain a
* {@code Type} instance for any given type, inline.
* </p>
* <p>Additionally {@link TypeLiteral} implements the {@link Typed} interface which
* is a generalization of this concept, and which may be implemented in custom classes.
* It is suggested that APIs be defined in terms of the interface, in the following manner:
* </p>
* <pre>
* &lt;T&gt; T obtain(Typed&lt;T&gt; typed, ...);
* </pre>
*
* @since 3.2
*/
public abstract class TypeLiteral<T> implements Typed<T> {
@SuppressWarnings("rawtypes")
private static final TypeVariable<Class<TypeLiteral>> T = TypeLiteral.class.getTypeParameters()[0];
/**
* Represented type.
*/
public final Type value;
private final String toString;
/**
* The default constructor.
*/
protected TypeLiteral() {
this.value =
Validate.notNull(TypeUtils.getTypeArguments(getClass(), TypeLiteral.class).get(T),
"%s does not assign type parameter %s", getClass(), TypeUtils.toLongString(T));
this.toString = String.format("%s<%s>", TypeLiteral.class.getSimpleName(), TypeUtils.toString(value));
}
@Override
public final boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof TypeLiteral == false) {
return false;
}
final TypeLiteral<?> other = (TypeLiteral<?>) obj;
return TypeUtils.equals(value, other.value);
}
@Override
public int hashCode() {
return 37 << 4 | value.hashCode();
}
@Override
public String toString() {
return toString;
}
@Override
public Type getType() {
return value;
}
}

View File

@ -1,34 +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 com.qihoo360.replugin.ext.lang3.reflect;
import java.lang.reflect.Type;
/**
* Generalization of "has a type."
* @see TypeLiteral
* @since 3.2
*/
public interface Typed<T> {
/**
* Get the {@link Type} represented by this entity.
*
* @return Type
*/
Type getType();
}

View File

@ -1,445 +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 com.qihoo360.replugin.ext.lang3.text;
import java.util.Arrays;
import com.qihoo360.replugin.ext.lang3.StringUtils;
/**
* A matcher class that can be queried to determine if a character array
* portion matches.
* <p>
* This class comes complete with various factory methods.
* If these do not suffice, you can subclass and implement your own matcher.
*
* @since 2.2
* @deprecated as of 3.6, use commons-text
* <a href="https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/StrMatcher.html">
* StrMatcher</a> instead
*/
@Deprecated
public abstract class StrMatcher {
/**
* Matches the comma character.
*/
private static final StrMatcher COMMA_MATCHER = new CharMatcher(',');
/**
* Matches the tab character.
*/
private static final StrMatcher TAB_MATCHER = new CharMatcher('\t');
/**
* Matches the space character.
*/
private static final StrMatcher SPACE_MATCHER = new CharMatcher(' ');
/**
* Matches the same characters as StringTokenizer,
* namely space, tab, newline, formfeed.
*/
private static final StrMatcher SPLIT_MATCHER = new CharSetMatcher(" \t\n\r\f".toCharArray());
/**
* Matches the String trim() whitespace characters.
*/
private static final StrMatcher TRIM_MATCHER = new TrimMatcher();
/**
* Matches the double quote character.
*/
private static final StrMatcher SINGLE_QUOTE_MATCHER = new CharMatcher('\'');
/**
* Matches the double quote character.
*/
private static final StrMatcher DOUBLE_QUOTE_MATCHER = new CharMatcher('"');
/**
* Matches the single or double quote character.
*/
private static final StrMatcher QUOTE_MATCHER = new CharSetMatcher("'\"".toCharArray());
/**
* Matches no characters.
*/
private static final StrMatcher NONE_MATCHER = new NoMatcher();
// -----------------------------------------------------------------------
/**
* Returns a matcher which matches the comma character.
*
* @return a matcher for a comma
*/
public static StrMatcher commaMatcher() {
return COMMA_MATCHER;
}
/**
* Returns a matcher which matches the tab character.
*
* @return a matcher for a tab
*/
public static StrMatcher tabMatcher() {
return TAB_MATCHER;
}
/**
* Returns a matcher which matches the space character.
*
* @return a matcher for a space
*/
public static StrMatcher spaceMatcher() {
return SPACE_MATCHER;
}
/**
* Matches the same characters as StringTokenizer,
* namely space, tab, newline and formfeed.
*
* @return the split matcher
*/
public static StrMatcher splitMatcher() {
return SPLIT_MATCHER;
}
/**
* Matches the String trim() whitespace characters.
*
* @return the trim matcher
*/
public static StrMatcher trimMatcher() {
return TRIM_MATCHER;
}
/**
* Returns a matcher which matches the single quote character.
*
* @return a matcher for a single quote
*/
public static StrMatcher singleQuoteMatcher() {
return SINGLE_QUOTE_MATCHER;
}
/**
* Returns a matcher which matches the double quote character.
*
* @return a matcher for a double quote
*/
public static StrMatcher doubleQuoteMatcher() {
return DOUBLE_QUOTE_MATCHER;
}
/**
* Returns a matcher which matches the single or double quote character.
*
* @return a matcher for a single or double quote
*/
public static StrMatcher quoteMatcher() {
return QUOTE_MATCHER;
}
/**
* Matches no characters.
*
* @return a matcher that matches nothing
*/
public static StrMatcher noneMatcher() {
return NONE_MATCHER;
}
/**
* Constructor that creates a matcher from a character.
*
* @param ch the character to match, must not be null
* @return a new Matcher for the given char
*/
public static StrMatcher charMatcher(final char ch) {
return new CharMatcher(ch);
}
/**
* Constructor that creates a matcher from a set of characters.
*
* @param chars the characters to match, null or empty matches nothing
* @return a new matcher for the given char[]
*/
public static StrMatcher charSetMatcher(final char... chars) {
if (chars == null || chars.length == 0) {
return NONE_MATCHER;
}
if (chars.length == 1) {
return new CharMatcher(chars[0]);
}
return new CharSetMatcher(chars);
}
/**
* Constructor that creates a matcher from a string representing a set of characters.
*
* @param chars the characters to match, null or empty matches nothing
* @return a new Matcher for the given characters
*/
public static StrMatcher charSetMatcher(final String chars) {
if (StringUtils.isEmpty(chars)) {
return NONE_MATCHER;
}
if (chars.length() == 1) {
return new CharMatcher(chars.charAt(0));
}
return new CharSetMatcher(chars.toCharArray());
}
/**
* Constructor that creates a matcher from a string.
*
* @param str the string to match, null or empty matches nothing
* @return a new Matcher for the given String
*/
public static StrMatcher stringMatcher(final String str) {
if (StringUtils.isEmpty(str)) {
return NONE_MATCHER;
}
return new StringMatcher(str);
}
//-----------------------------------------------------------------------
/**
* Constructor.
*/
protected StrMatcher() {
super();
}
/**
* Returns the number of matching characters, zero for no match.
* <p>
* This method is called to check for a match.
* The parameter <code>pos</code> represents the current position to be
* checked in the string <code>buffer</code> (a character array which must
* not be changed).
* The API guarantees that <code>pos</code> is a valid index for <code>buffer</code>.
* <p>
* The character array may be larger than the active area to be matched.
* Only values in the buffer between the specified indices may be accessed.
* <p>
* The matching code may check one character or many.
* It may check characters preceding <code>pos</code> as well as those
* after, so long as no checks exceed the bounds specified.
* <p>
* It must return zero for no match, or a positive number if a match was found.
* The number indicates the number of characters that matched.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index (exclusive) of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
public abstract int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd);
/**
* Returns the number of matching characters, zero for no match.
* <p>
* This method is called to check for a match.
* The parameter <code>pos</code> represents the current position to be
* checked in the string <code>buffer</code> (a character array which must
* not be changed).
* The API guarantees that <code>pos</code> is a valid index for <code>buffer</code>.
* <p>
* The matching code may check one character or many.
* It may check characters preceding <code>pos</code> as well as those after.
* <p>
* It must return zero for no match, or a positive number if a match was found.
* The number indicates the number of characters that matched.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @return the number of matching characters, zero for no match
* @since 2.4
*/
public int isMatch(final char[] buffer, final int pos) {
return isMatch(buffer, pos, 0, buffer.length);
}
//-----------------------------------------------------------------------
/**
* Class used to define a set of characters for matching purposes.
*/
static final class CharSetMatcher extends StrMatcher {
/** The set of characters to match. */
private final char[] chars;
/**
* Constructor that creates a matcher from a character array.
*
* @param chars the characters to match, must not be null
*/
CharSetMatcher(final char chars[]) {
super();
this.chars = chars.clone();
Arrays.sort(this.chars);
}
/**
* Returns whether or not the given character matches.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
@Override
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
return Arrays.binarySearch(chars, buffer[pos]) >= 0 ? 1 : 0;
}
}
//-----------------------------------------------------------------------
/**
* Class used to define a character for matching purposes.
*/
static final class CharMatcher extends StrMatcher {
/** The character to match. */
private final char ch;
/**
* Constructor that creates a matcher that matches a single character.
*
* @param ch the character to match
*/
CharMatcher(final char ch) {
super();
this.ch = ch;
}
/**
* Returns whether or not the given character matches.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
@Override
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
return ch == buffer[pos] ? 1 : 0;
}
}
//-----------------------------------------------------------------------
/**
* Class used to define a set of characters for matching purposes.
*/
static final class StringMatcher extends StrMatcher {
/** The string to match, as a character array. */
private final char[] chars;
/**
* Constructor that creates a matcher from a String.
*
* @param str the string to match, must not be null
*/
StringMatcher(final String str) {
super();
chars = str.toCharArray();
}
/**
* Returns whether or not the given text matches the stored string.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
@Override
public int isMatch(final char[] buffer, int pos, final int bufferStart, final int bufferEnd) {
final int len = chars.length;
if (pos + len > bufferEnd) {
return 0;
}
for (int i = 0; i < chars.length; i++, pos++) {
if (chars[i] != buffer[pos]) {
return 0;
}
}
return len;
}
@Override
public String toString() {
return super.toString() + ' ' + Arrays.toString(chars);
}
}
//-----------------------------------------------------------------------
/**
* Class used to match no characters.
*/
static final class NoMatcher extends StrMatcher {
/**
* Constructs a new instance of <code>NoMatcher</code>.
*/
NoMatcher() {
super();
}
/**
* Always returns <code>false</code>.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
@Override
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
return 0;
}
}
//-----------------------------------------------------------------------
/**
* Class used to match whitespace as per trim().
*/
static final class TrimMatcher extends StrMatcher {
/**
* Constructs a new instance of <code>TrimMatcher</code>.
*/
TrimMatcher() {
super();
}
/**
* Returns whether or not the given character matches.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
@Override
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
return buffer[pos] <= 32 ? 1 : 0;
}
}
}

View File

@ -1,739 +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 com.qihoo360.replugin.ext.lang3.text;
import com.qihoo360.replugin.ext.lang3.ArrayUtils;
import com.qihoo360.replugin.ext.lang3.StringUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <p>Operations on Strings that contain words.</p>
*
* <p>This class tries to handle <code>null</code> input gracefully.
* An exception will not be thrown for a <code>null</code> input.
* Each method documents its behaviour in more detail.</p>
*
* @since 2.0
* @deprecated as of 3.6, use commons-text
* <a href="https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/WordUtils.html">
* WordUtils</a> instead
*/
@Deprecated
public class WordUtils {
/**
* <p><code>WordUtils</code> instances should NOT be constructed in
* standard programming. Instead, the class should be used as
* <code>WordUtils.wrap("foo bar", 20);</code>.</p>
*
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
*/
public WordUtils() {
super();
}
// Wrapping
//--------------------------------------------------------------------------
/**
* <p>Wraps a single line of text, identifying words by <code>' '</code>.</p>
*
* <p>New lines will be separated by the system property line separator.
* Very long words, such as URLs will <i>not</i> be wrapped.</p>
*
* <p>Leading spaces on a new line are stripped.
* Trailing spaces are not stripped.</p>
*
* <table border="1" summary="Wrap Results">
* <tr>
* <th>input</th>
* <th>wrapLength</th>
* <th>result</th>
* </tr>
* <tr>
* <td>null</td>
* <td>*</td>
* <td>null</td>
* </tr>
* <tr>
* <td>""</td>
* <td>*</td>
* <td>""</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
* <td>20</td>
* <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td>
* </tr>
* <tr>
* <td>"Click here to jump to the commons website - http://commons.apache.org"</td>
* <td>20</td>
* <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org"</td>
* </tr>
* <tr>
* <td>"Click here, http://commons.apache.org, to jump to the commons website"</td>
* <td>20</td>
* <td>"Click here,\nhttp://commons.apache.org,\nto jump to the\ncommons website"</td>
* </tr>
* </table>
*
* (assuming that '\n' is the systems line separator)
*
* @param str the String to be word wrapped, may be null
* @param wrapLength the column to wrap the words at, less than 1 is treated as 1
* @return a line with newlines inserted, <code>null</code> if null input
*/
public static String wrap(final String str, final int wrapLength) {
return wrap(str, wrapLength, null, false);
}
/**
* <p>Wraps a single line of text, identifying words by <code>' '</code>.</p>
*
* <p>Leading spaces on a new line are stripped.
* Trailing spaces are not stripped.</p>
*
* <table border="1" summary="Wrap Results">
* <tr>
* <th>input</th>
* <th>wrapLength</th>
* <th>newLineString</th>
* <th>wrapLongWords</th>
* <th>result</th>
* </tr>
* <tr>
* <td>null</td>
* <td>*</td>
* <td>*</td>
* <td>true/false</td>
* <td>null</td>
* </tr>
* <tr>
* <td>""</td>
* <td>*</td>
* <td>*</td>
* <td>true/false</td>
* <td>""</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
* <td>20</td>
* <td>"\n"</td>
* <td>true/false</td>
* <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
* <td>20</td>
* <td>"&lt;br /&gt;"</td>
* <td>true/false</td>
* <td>"Here is one line of&lt;br /&gt;text that is going&lt;br /&gt;to be wrapped after&lt;br /&gt;20 columns."</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
* <td>20</td>
* <td>null</td>
* <td>true/false</td>
* <td>"Here is one line of" + systemNewLine + "text that is going" + systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."</td>
* </tr>
* <tr>
* <td>"Click here to jump to the commons website - http://commons.apache.org"</td>
* <td>20</td>
* <td>"\n"</td>
* <td>false</td>
* <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org"</td>
* </tr>
* <tr>
* <td>"Click here to jump to the commons website - http://commons.apache.org"</td>
* <td>20</td>
* <td>"\n"</td>
* <td>true</td>
* <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apach\ne.org"</td>
* </tr>
* </table>
*
* @param str the String to be word wrapped, may be null
* @param wrapLength the column to wrap the words at, less than 1 is treated as 1
* @param newLineStr the string to insert for a new line,
* <code>null</code> uses the system property line separator
* @param wrapLongWords true if long words (such as URLs) should be wrapped
* @return a line with newlines inserted, <code>null</code> if null input
*/
public static String wrap(final String str, final int wrapLength, final String newLineStr, final boolean wrapLongWords) {
return wrap(str, wrapLength, newLineStr, wrapLongWords, " ");
}
/**
* <p>Wraps a single line of text, identifying words by <code>wrapOn</code>.</p>
*
* <p>Leading spaces on a new line are stripped.
* Trailing spaces are not stripped.</p>
*
* <table border="1" summary="Wrap Results">
* <tr>
* <th>input</th>
* <th>wrapLength</th>
* <th>newLineString</th>
* <th>wrapLongWords</th>
* <th>wrapOn</th>
* <th>result</th>
* </tr>
* <tr>
* <td>null</td>
* <td>*</td>
* <td>*</td>
* <td>true/false</td>
* <td>*</td>
* <td>null</td>
* </tr>
* <tr>
* <td>""</td>
* <td>*</td>
* <td>*</td>
* <td>true/false</td>
* <td>*</td>
* <td>""</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
* <td>20</td>
* <td>"\n"</td>
* <td>true/false</td>
* <td>" "</td>
* <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
* <td>20</td>
* <td>"&lt;br /&gt;"</td>
* <td>true/false</td>
* <td>" "</td>
* <td>"Here is one line of&lt;br /&gt;text that is going&lt;br /&gt;to be wrapped after&lt;br /&gt;20 columns."</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
* <td>20</td>
* <td>null</td>
* <td>true/false</td>
* <td>" "</td>
* <td>"Here is one line of" + systemNewLine + "text that is going" + systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."</td>
* </tr>
* <tr>
* <td>"Click here to jump to the commons website - http://commons.apache.org"</td>
* <td>20</td>
* <td>"\n"</td>
* <td>false</td>
* <td>" "</td>
* <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org"</td>
* </tr>
* <tr>
* <td>"Click here to jump to the commons website - http://commons.apache.org"</td>
* <td>20</td>
* <td>"\n"</td>
* <td>true</td>
* <td>" "</td>
* <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apach\ne.org"</td>
* </tr>
* <tr>
* <td>"flammable/inflammable"</td>
* <td>20</td>
* <td>"\n"</td>
* <td>true</td>
* <td>"/"</td>
* <td>"flammable\ninflammable"</td>
* </tr>
* </table>
* @param str the String to be word wrapped, may be null
* @param wrapLength the column to wrap the words at, less than 1 is treated as 1
* @param newLineStr the string to insert for a new line,
* <code>null</code> uses the system property line separator
* @param wrapLongWords true if long words (such as URLs) should be wrapped
* @param wrapOn regex expression to be used as a breakable characters,
* if blank string is provided a space character will be used
* @return a line with newlines inserted, <code>null</code> if null input
*/
public static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords, String wrapOn) {
if (str == null) {
return null;
}
if (newLineStr == null) {
newLineStr = System.lineSeparator();
}
if (wrapLength < 1) {
wrapLength = 1;
}
if (StringUtils.isBlank(wrapOn)) {
wrapOn = " ";
}
final Pattern patternToWrapOn = Pattern.compile(wrapOn);
final int inputLineLength = str.length();
int offset = 0;
final StringBuilder wrappedLine = new StringBuilder(inputLineLength + 32);
while (offset < inputLineLength) {
int spaceToWrapAt = -1;
Matcher matcher = patternToWrapOn.matcher(str.substring(offset, Math.min(offset + wrapLength + 1, inputLineLength)));
if (matcher.find()) {
if (matcher.start() == 0) {
offset += matcher.end();
continue;
}
spaceToWrapAt = matcher.start() + offset;
}
// only last line without leading spaces is left
if(inputLineLength - offset <= wrapLength) {
break;
}
while(matcher.find()){
spaceToWrapAt = matcher.start() + offset;
}
if (spaceToWrapAt >= offset) {
// normal case
wrappedLine.append(str.substring(offset, spaceToWrapAt));
wrappedLine.append(newLineStr);
offset = spaceToWrapAt + 1;
} else {
// really long word or URL
if (wrapLongWords) {
// wrap really long word one line at a time
wrappedLine.append(str.substring(offset, wrapLength + offset));
wrappedLine.append(newLineStr);
offset += wrapLength;
} else {
// do not wrap really long word, just extend beyond limit
matcher = patternToWrapOn.matcher(str.substring(offset + wrapLength));
if (matcher.find()) {
spaceToWrapAt = matcher.start() + offset + wrapLength;
}
if (spaceToWrapAt >= 0) {
wrappedLine.append(str.substring(offset, spaceToWrapAt));
wrappedLine.append(newLineStr);
offset = spaceToWrapAt + 1;
} else {
wrappedLine.append(str.substring(offset));
offset = inputLineLength;
}
}
}
}
// Whatever is left in line is short enough to just pass through
wrappedLine.append(str.substring(offset));
return wrappedLine.toString();
}
// Capitalizing
//-----------------------------------------------------------------------
/**
* <p>Capitalizes all the whitespace separated words in a String.
* Only the first character of each word is changed. To convert the
* rest of each word to lowercase at the same time,
* use {@link #capitalizeFully(String)}.</p>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A <code>null</code> input String returns <code>null</code>.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* WordUtils.capitalize(null) = null
* WordUtils.capitalize("") = ""
* WordUtils.capitalize("i am FINE") = "I Am FINE"
* </pre>
*
* @param str the String to capitalize, may be null
* @return capitalized String, <code>null</code> if null String input
* @see #uncapitalize(String)
* @see #capitalizeFully(String)
*/
public static String capitalize(final String str) {
return capitalize(str, null);
}
/**
* <p>Capitalizes all the delimiter separated words in a String.
* Only the first character of each word is changed. To convert the
* rest of each word to lowercase at the same time,
* use {@link #capitalizeFully(String, char[])}.</p>
*
* <p>The delimiters represent a set of characters understood to separate words.
* The first string character and the first non-delimiter character after a
* delimiter will be capitalized. </p>
*
* <p>A <code>null</code> input String returns <code>null</code>.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* WordUtils.capitalize(null, *) = null
* WordUtils.capitalize("", *) = ""
* WordUtils.capitalize(*, new char[0]) = *
* WordUtils.capitalize("i am fine", null) = "I Am Fine"
* WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine"
* </pre>
*
* @param str the String to capitalize, may be null
* @param delimiters set of characters to determine capitalization, null means whitespace
* @return capitalized String, <code>null</code> if null String input
* @see #uncapitalize(String)
* @see #capitalizeFully(String)
* @since 2.1
*/
public static String capitalize(final String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
final char[] buffer = str.toCharArray();
boolean capitalizeNext = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (isDelimiter(ch, delimiters)) {
capitalizeNext = true;
} else if (capitalizeNext) {
buffer[i] = Character.toTitleCase(ch);
capitalizeNext = false;
}
}
return new String(buffer);
}
//-----------------------------------------------------------------------
/**
* <p>Converts all the whitespace separated words in a String into capitalized words,
* that is each word is made up of a titlecase character and then a series of
* lowercase characters. </p>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A <code>null</code> input String returns <code>null</code>.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* WordUtils.capitalizeFully(null) = null
* WordUtils.capitalizeFully("") = ""
* WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
* </pre>
*
* @param str the String to capitalize, may be null
* @return capitalized String, <code>null</code> if null String input
*/
public static String capitalizeFully(final String str) {
return capitalizeFully(str, null);
}
/**
* <p>Converts all the delimiter separated words in a String into capitalized words,
* that is each word is made up of a titlecase character and then a series of
* lowercase characters. </p>
*
* <p>The delimiters represent a set of characters understood to separate words.
* The first string character and the first non-delimiter character after a
* delimiter will be capitalized. </p>
*
* <p>A <code>null</code> input String returns <code>null</code>.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* WordUtils.capitalizeFully(null, *) = null
* WordUtils.capitalizeFully("", *) = ""
* WordUtils.capitalizeFully(*, null) = *
* WordUtils.capitalizeFully(*, new char[0]) = *
* WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
* </pre>
*
* @param str the String to capitalize, may be null
* @param delimiters set of characters to determine capitalization, null means whitespace
* @return capitalized String, <code>null</code> if null String input
* @since 2.1
*/
public static String capitalizeFully(String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
str = str.toLowerCase();
return capitalize(str, delimiters);
}
//-----------------------------------------------------------------------
/**
* <p>Uncapitalizes all the whitespace separated words in a String.
* Only the first character of each word is changed.</p>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A <code>null</code> input String returns <code>null</code>.</p>
*
* <pre>
* WordUtils.uncapitalize(null) = null
* WordUtils.uncapitalize("") = ""
* WordUtils.uncapitalize("I Am FINE") = "i am fINE"
* </pre>
*
* @param str the String to uncapitalize, may be null
* @return uncapitalized String, <code>null</code> if null String input
* @see #capitalize(String)
*/
public static String uncapitalize(final String str) {
return uncapitalize(str, null);
}
/**
* <p>Uncapitalizes all the whitespace separated words in a String.
* Only the first character of each word is changed.</p>
*
* <p>The delimiters represent a set of characters understood to separate words.
* The first string character and the first non-delimiter character after a
* delimiter will be uncapitalized. </p>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A <code>null</code> input String returns <code>null</code>.</p>
*
* <pre>
* WordUtils.uncapitalize(null, *) = null
* WordUtils.uncapitalize("", *) = ""
* WordUtils.uncapitalize(*, null) = *
* WordUtils.uncapitalize(*, new char[0]) = *
* WordUtils.uncapitalize("I AM.FINE", {'.'}) = "i AM.fINE"
* </pre>
*
* @param str the String to uncapitalize, may be null
* @param delimiters set of characters to determine uncapitalization, null means whitespace
* @return uncapitalized String, <code>null</code> if null String input
* @see #capitalize(String)
* @since 2.1
*/
public static String uncapitalize(final String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
final char[] buffer = str.toCharArray();
boolean uncapitalizeNext = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (isDelimiter(ch, delimiters)) {
uncapitalizeNext = true;
} else if (uncapitalizeNext) {
buffer[i] = Character.toLowerCase(ch);
uncapitalizeNext = false;
}
}
return new String(buffer);
}
//-----------------------------------------------------------------------
/**
* <p>Swaps the case of a String using a word based algorithm.</p>
*
* <ul>
* <li>Upper case character converts to Lower case</li>
* <li>Title case character converts to Lower case</li>
* <li>Lower case character after Whitespace or at start converts to Title case</li>
* <li>Other Lower case character converts to Upper case</li>
* </ul>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A <code>null</code> input String returns <code>null</code>.</p>
*
* <pre>
* StringUtils.swapCase(null) = null
* StringUtils.swapCase("") = ""
* StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
* </pre>
*
* @param str the String to swap case, may be null
* @return the changed String, <code>null</code> if null String input
*/
public static String swapCase(final String str) {
if (StringUtils.isEmpty(str)) {
return str;
}
final char[] buffer = str.toCharArray();
boolean whitespace = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (Character.isUpperCase(ch)) {
buffer[i] = Character.toLowerCase(ch);
whitespace = false;
} else if (Character.isTitleCase(ch)) {
buffer[i] = Character.toLowerCase(ch);
whitespace = false;
} else if (Character.isLowerCase(ch)) {
if (whitespace) {
buffer[i] = Character.toTitleCase(ch);
whitespace = false;
} else {
buffer[i] = Character.toUpperCase(ch);
}
} else {
whitespace = Character.isWhitespace(ch);
}
}
return new String(buffer);
}
//-----------------------------------------------------------------------
/**
* <p>Extracts the initial characters from each word in the String.</p>
*
* <p>All first characters after whitespace are returned as a new string.
* Their case is not changed.</p>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A <code>null</code> input String returns <code>null</code>.</p>
*
* <pre>
* WordUtils.initials(null) = null
* WordUtils.initials("") = ""
* WordUtils.initials("Ben John Lee") = "BJL"
* WordUtils.initials("Ben J.Lee") = "BJ"
* </pre>
*
* @param str the String to get initials from, may be null
* @return String of initial letters, <code>null</code> if null String input
* @see #initials(String,char[])
* @since 2.2
*/
public static String initials(final String str) {
return initials(str, null);
}
/**
* <p>Extracts the initial characters from each word in the String.</p>
*
* <p>All first characters after the defined delimiters are returned as a new string.
* Their case is not changed.</p>
*
* <p>If the delimiters array is null, then Whitespace is used.
* Whitespace is defined by {@link Character#isWhitespace(char)}.
* A <code>null</code> input String returns <code>null</code>.
* An empty delimiter array returns an empty String.</p>
*
* <pre>
* WordUtils.initials(null, *) = null
* WordUtils.initials("", *) = ""
* WordUtils.initials("Ben John Lee", null) = "BJL"
* WordUtils.initials("Ben J.Lee", null) = "BJ"
* WordUtils.initials("Ben J.Lee", [' ','.']) = "BJL"
* WordUtils.initials(*, new char[0]) = ""
* </pre>
*
* @param str the String to get initials from, may be null
* @param delimiters set of characters to determine words, null means whitespace
* @return String of initial characters, <code>null</code> if null String input
* @see #initials(String)
* @since 2.2
*/
public static String initials(final String str, final char... delimiters) {
if (StringUtils.isEmpty(str)) {
return str;
}
if (delimiters != null && delimiters.length == 0) {
return StringUtils.EMPTY;
}
final int strLen = str.length();
final char[] buf = new char[strLen / 2 + 1];
int count = 0;
boolean lastWasGap = true;
for (int i = 0; i < strLen; i++) {
final char ch = str.charAt(i);
if (isDelimiter(ch, delimiters)) {
lastWasGap = true;
} else if (lastWasGap) {
buf[count++] = ch;
lastWasGap = false;
} else {
continue; // ignore ch
}
}
return new String(buf, 0, count);
}
//-----------------------------------------------------------------------
/**
* <p>Checks if the String contains all words in the given array.</p>
*
* <p>
* A {@code null} String will return {@code false}. A {@code null}, zero
* length search array or if one element of array is null will return {@code false}.
* </p>
*
* <pre>
* WordUtils.containsAllWords(null, *) = false
* WordUtils.containsAllWords("", *) = false
* WordUtils.containsAllWords(*, null) = false
* WordUtils.containsAllWords(*, []) = false
* WordUtils.containsAllWords("abcd", "ab", "cd") = false
* WordUtils.containsAllWords("abc def", "def", "abc") = true
* </pre>
*
*
* @param word The CharSequence to check, may be null
* @param words The array of String words to search for, may be null
* @return {@code true} if all search words are found, {@code false} otherwise
* @since 3.5
*/
public static boolean containsAllWords(final CharSequence word, final CharSequence... words) {
if (StringUtils.isEmpty(word) || ArrayUtils.isEmpty(words)) {
return false;
}
for (final CharSequence w : words) {
if (StringUtils.isBlank(w)) {
return false;
}
final Pattern p = Pattern.compile(".*\\b" + w + "\\b.*");
if (!p.matcher(word).matches()) {
return false;
}
}
return true;
}
//-----------------------------------------------------------------------
/**
* Is the character a delimiter.
*
* @param ch the character to check
* @param delimiters the delimiters
* @return true if it is a delimiter
*/
private static boolean isDelimiter(final char ch, final char[] delimiters) {
if (delimiters == null) {
return Character.isWhitespace(ch);
}
for (final char delimiter : delimiters) {
if (ch == delimiter) {
return true;
}
}
return false;
}
}

View File

@ -1,127 +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 com.qihoo360.replugin.ext.lang3.time;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* DateParser is the "missing" interface for the parsing methods of
* {@link java.text.DateFormat}. You can obtain an object implementing this
* interface by using one of the FastDateFormat factory methods.
* <p>
* Warning: Since binary compatible methods may be added to this interface in any
* release, developers are not expected to implement this interface.
*
* @since 3.2
*/
public interface DateParser {
/**
* Equivalent to DateFormat.parse(String).
*
* See {@link java.text.DateFormat#parse(String)} for more information.
* @param source A <code>String</code> whose beginning should be parsed.
* @return A <code>Date</code> parsed from the string
* @throws ParseException if the beginning of the specified string cannot be parsed.
*/
Date parse(String source) throws ParseException;
/**
* Equivalent to DateFormat.parse(String, ParsePosition).
*
* See {@link java.text.DateFormat#parse(String, ParsePosition)} for more information.
*
* @param source A <code>String</code>, part of which should be parsed.
* @param pos A <code>ParsePosition</code> object with index and error index information
* as described above.
* @return A <code>Date</code> parsed from the string. In case of error, returns null.
* @throws NullPointerException if text or pos is null.
*/
Date parse(String source, ParsePosition pos);
/**
* Parses a formatted date string according to the format. Updates the Calendar with parsed fields.
* Upon success, the ParsePosition index is updated to indicate how much of the source text was consumed.
* Not all source text needs to be consumed. Upon parse failure, ParsePosition error index is updated to
* the offset of the source text which does not match the supplied format.
*
* @param source The text to parse.
* @param pos On input, the position in the source to start parsing, on output, updated position.
* @param calendar The calendar into which to set parsed fields.
* @return true, if source has been parsed (pos parsePosition is updated); otherwise false (and pos errorIndex is updated)
* @throws IllegalArgumentException when Calendar has been set to be not lenient, and a parsed field is
* out of range.
*
* @since 3.5
*/
boolean parse(String source, ParsePosition pos, Calendar calendar);
// Accessors
//-----------------------------------------------------------------------
/**
* <p>Gets the pattern used by this parser.</p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
String getPattern();
/**
* <p>
* Gets the time zone used by this parser.
* </p>
*
* <p>
* The default {@link TimeZone} used to create a {@link Date} when the {@link TimeZone} is not specified by
* the format pattern.
* </p>
*
* @return the time zone
*/
TimeZone getTimeZone();
/**
* <p>Gets the locale used by this parser.</p>
*
* @return the locale
*/
Locale getLocale();
/**
* Parses text from a string to produce a Date.
*
* @param source A <code>String</code> whose beginning should be parsed.
* @return a <code>java.util.Date</code> object
* @throws ParseException if the beginning of the specified string cannot be parsed.
* @see java.text.DateFormat#parseObject(String)
*/
Object parseObject(String source) throws ParseException;
/**
* Parses a date/time string according to the given parse position.
*
* @param source A <code>String</code> whose beginning should be parsed.
* @param pos the parse position
* @return a <code>java.util.Date</code> object
* @see java.text.DateFormat#parseObject(String, ParsePosition)
*/
Object parseObject(String source, ParsePosition pos);
}

View File

@ -1,178 +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 com.qihoo360.replugin.ext.lang3.time;
import java.text.FieldPosition;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* DatePrinter is the "missing" interface for the format methods of
* {@link java.text.DateFormat}. You can obtain an object implementing this
* interface by using one of the FastDateFormat factory methods.
* <p>
* Warning: Since binary compatible methods may be added to this interface in any
* release, developers are not expected to implement this interface.
*
* @since 3.2
*/
public interface DatePrinter {
/**
* <p>Formats a millisecond {@code long} value.</p>
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/
String format(long millis);
/**
* <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @return the formatted string
*/
String format(Date date);
/**
* <p>Formats a {@code Calendar} object.</p>
* The TimeZone set on the Calendar is only used to adjust the time offset.
* The TimeZone specified during the construction of the Parser will determine the TimeZone
* used in the formatted string.
*
* @param calendar the calendar to format.
* @return the formatted string
*/
String format(Calendar calendar);
/**
* <p>Formats a millisecond {@code long} value into the
* supplied {@code StringBuffer}.</p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(long, Appendable)}.
*/
@Deprecated
StringBuffer format(long millis, StringBuffer buf);
/**
* <p>Formats a {@code Date} object into the
* supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Date, Appendable)}.
*/
@Deprecated
StringBuffer format(Date date, StringBuffer buf);
/**
* <p>Formats a {@code Calendar} object into the supplied {@code StringBuffer}.</p>
* The TimeZone set on the Calendar is only used to adjust the time offset.
* The TimeZone specified during the construction of the Parser will determine the TimeZone
* used in the formatted string.
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Calendar, Appendable)}.
*/
@Deprecated
StringBuffer format(Calendar calendar, StringBuffer buf);
/**
* <p>Formats a millisecond {@code long} value into the
* supplied {@code Appendable}.</p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or StringBuffer.
* @return the specified string buffer
* @since 3.5
*/
<B extends Appendable> B format(long millis, B buf);
/**
* <p>Formats a {@code Date} object into the
* supplied {@code Appendable} using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or StringBuffer.
* @return the specified string buffer
* @since 3.5
*/
<B extends Appendable> B format(Date date, B buf);
/**
* <p>Formats a {@code Calendar} object into the supplied {@code Appendable}.</p>
* The TimeZone set on the Calendar is only used to adjust the time offset.
* The TimeZone specified during the construction of the Parser will determine the TimeZone
* used in the formatted string.
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or StringBuffer.
* @return the specified string buffer
* @since 3.5
*/
<B extends Appendable> B format(Calendar calendar, B buf);
// Accessors
//-----------------------------------------------------------------------
/**
* <p>Gets the pattern used by this printer.</p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
String getPattern();
/**
* <p>Gets the time zone used by this printer.</p>
*
* <p>This zone is always used for {@code Date} printing. </p>
*
* @return the time zone
*/
TimeZone getTimeZone();
/**
* <p>Gets the locale used by this printer.</p>
*
* @return the locale
*/
Locale getLocale();
/**
* <p>Formats a {@code Date}, {@code Calendar} or
* {@code Long} (milliseconds) object.</p>
*
* @param obj the object to format
* @param toAppendTo the buffer to append to
* @param pos the position - ignored
* @return the buffer passed in
* @see java.text.DateFormat#format(Object, StringBuffer, FieldPosition)
*/
StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos);
}

View File

@ -1,676 +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 com.qihoo360.replugin.ext.lang3.time;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* <p>FastDateFormat is a fast and thread-safe version of
* {@link java.text.SimpleDateFormat}.</p>
*
* <p>To obtain an instance of FastDateFormat, use one of the static factory methods:
* {@link #getInstance(String, TimeZone, Locale)}, {@link #getDateInstance(int, TimeZone, Locale)},
* {@link #getTimeInstance(int, TimeZone, Locale)}, or {@link #getDateTimeInstance(int, int, TimeZone, Locale)}
* </p>
*
* <p>Since FastDateFormat is thread safe, you can use a static member instance:</p>
* <code>
* private static final FastDateFormat DATE_FORMATTER = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.SHORT);
* </code>
*
* <p>This class can be used as a direct replacement to
* {@code SimpleDateFormat} in most formatting and parsing situations.
* This class is especially useful in multi-threaded server environments.
* {@code SimpleDateFormat} is not thread-safe in any JDK version,
* nor will it be as Sun have closed the bug/RFE.
* </p>
*
* <p>All patterns are compatible with
* SimpleDateFormat (except time zones and some year patterns - see below).</p>
*
* <p>Since 3.2, FastDateFormat supports parsing as well as printing.</p>
*
* <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent
* time zones in RFC822 format (eg. {@code +0800} or {@code -1100}).
* This pattern letter can be used here (on all JDK versions).</p>
*
* <p>In addition, the pattern {@code 'ZZ'} has been made to represent
* ISO 8601 extended format time zones (eg. {@code +08:00} or {@code -11:00}).
* This introduces a minor incompatibility with Java 1.4, but at a gain of
* useful functionality.</p>
*
* <p>Javadoc cites for the year pattern: <i>For formatting, if the number of
* pattern letters is 2, the year is truncated to 2 digits; otherwise it is
* interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or
* 'YYY' will be formatted as '2003', while it was '03' in former Java
* versions. FastDateFormat implements the behavior of Java 7.</p>
*
* @since 2.0
*/
public class FastDateFormat extends Format implements DateParser, DatePrinter {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 2L;
/**
* FULL locale dependent date or time style.
*/
public static final int FULL = DateFormat.FULL;
/**
* LONG locale dependent date or time style.
*/
public static final int LONG = DateFormat.LONG;
/**
* MEDIUM locale dependent date or time style.
*/
public static final int MEDIUM = DateFormat.MEDIUM;
/**
* SHORT locale dependent date or time style.
*/
public static final int SHORT = DateFormat.SHORT;
private static final FormatCache<FastDateFormat> cache= new FormatCache<FastDateFormat>() {
@Override
protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return new FastDateFormat(pattern, timeZone, locale);
}
};
private final FastDatePrinter printer;
private final FastDateParser parser;
//-----------------------------------------------------------------------
/**
* <p>Gets a formatter instance using the default pattern in the
* default locale.</p>
*
* @return a date/time formatter
*/
public static FastDateFormat getInstance() {
return cache.getInstance();
}
/**
* <p>Gets a formatter instance using the specified pattern in the
* default locale.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern) {
return cache.getInstance(pattern, null, null);
}
/**
* <p>Gets a formatter instance using the specified pattern and
* time zone.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) {
return cache.getInstance(pattern, timeZone, null);
}
/**
* <p>Gets a formatter instance using the specified pattern and
* locale.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param locale optional locale, overrides system locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern, final Locale locale) {
return cache.getInstance(pattern, null, locale);
}
/**
* <p>Gets a formatter instance using the specified pattern, time zone
* and locale.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @param locale optional locale, overrides system locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or {@code null}
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return cache.getInstance(pattern, timeZone, locale);
}
//-----------------------------------------------------------------------
/**
* <p>Gets a date formatter instance using the specified style in the
* default time zone and locale.</p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateInstance(final int style) {
return cache.getDateInstance(style, null, null);
}
/**
* <p>Gets a date formatter instance using the specified style and
* locale in the default time zone.</p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param locale optional locale, overrides system locale
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateInstance(final int style, final Locale locale) {
return cache.getDateInstance(style, null, locale);
}
/**
* <p>Gets a date formatter instance using the specified style and
* time zone in the default locale.</p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) {
return cache.getDateInstance(style, timeZone, null);
}
/**
* <p>Gets a date formatter instance using the specified style, time
* zone and locale.</p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @param locale optional locale, overrides system locale
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date
* pattern defined
*/
public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) {
return cache.getDateInstance(style, timeZone, locale);
}
//-----------------------------------------------------------------------
/**
* <p>Gets a time formatter instance using the specified style in the
* default time zone and locale.</p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getTimeInstance(final int style) {
return cache.getTimeInstance(style, null, null);
}
/**
* <p>Gets a time formatter instance using the specified style and
* locale in the default time zone.</p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param locale optional locale, overrides system locale
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getTimeInstance(final int style, final Locale locale) {
return cache.getTimeInstance(style, null, locale);
}
/**
* <p>Gets a time formatter instance using the specified style and
* time zone in the default locale.</p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted time
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) {
return cache.getTimeInstance(style, timeZone, null);
}
/**
* <p>Gets a time formatter instance using the specified style, time
* zone and locale.</p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted time
* @param locale optional locale, overrides system locale
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time
* pattern defined
*/
public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) {
return cache.getTimeInstance(style, timeZone, locale);
}
//-----------------------------------------------------------------------
/**
* <p>Gets a date/time formatter instance using the specified style
* in the default time zone and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) {
return cache.getDateTimeInstance(dateStyle, timeStyle, null, null);
}
/**
* <p>Gets a date/time formatter instance using the specified style and
* locale in the default time zone.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) {
return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale);
}
/**
* <p>Gets a date/time formatter instance using the specified style and
* time zone in the default locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) {
return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
}
/**
* <p>Gets a date/time formatter instance using the specified style,
* time zone and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
*/
public static FastDateFormat getDateTimeInstance(
final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
}
// Constructor
//-----------------------------------------------------------------------
/**
* <p>Constructs a new FastDateFormat.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale to use
* @throws NullPointerException if pattern, timeZone, or locale is null.
*/
protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) {
this(pattern, timeZone, locale, null);
}
// Constructor
//-----------------------------------------------------------------------
/**
* <p>Constructs a new FastDateFormat.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale to use
* @param centuryStart The start of the 100 year period to use as the "default century" for 2 digit year parsing. If centuryStart is null, defaults to now - 80 years
* @throws NullPointerException if pattern, timeZone, or locale is null.
*/
protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
printer= new FastDatePrinter(pattern, timeZone, locale);
parser= new FastDateParser(pattern, timeZone, locale, centuryStart);
}
// Format methods
//-----------------------------------------------------------------------
/**
* <p>Formats a {@code Date}, {@code Calendar} or
* {@code Long} (milliseconds) object.</p>
* This method is an implementation of {@link Format#format(Object, StringBuffer, FieldPosition)}
*
* @param obj the object to format
* @param toAppendTo the buffer to append to
* @param pos the position - ignored
* @return the buffer passed in
*/
@Override
public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
return toAppendTo.append(printer.format(obj));
}
/**
* <p>Formats a millisecond {@code long} value.</p>
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/
@Override
public String format(final long millis) {
return printer.format(millis);
}
/**
* <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @return the formatted string
*/
@Override
public String format(final Date date) {
return printer.format(date);
}
/**
* <p>Formats a {@code Calendar} object.</p>
*
* @param calendar the calendar to format
* @return the formatted string
*/
@Override
public String format(final Calendar calendar) {
return printer.format(calendar);
}
/**
* <p>Formats a millisecond {@code long} value into the
* supplied {@code StringBuffer}.</p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 2.1
* @deprecated Use {{@link #format(long, Appendable)}.
*/
@Deprecated
@Override
public StringBuffer format(final long millis, final StringBuffer buf) {
return printer.format(millis, buf);
}
/**
* <p>Formats a {@code Date} object into the
* supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Date, Appendable)}.
*/
@Deprecated
@Override
public StringBuffer format(final Date date, final StringBuffer buf) {
return printer.format(date, buf);
}
/**
* <p>Formats a {@code Calendar} object into the
* supplied {@code StringBuffer}.</p>
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Calendar, Appendable)}.
*/
@Deprecated
@Override
public StringBuffer format(final Calendar calendar, final StringBuffer buf) {
return printer.format(calendar, buf);
}
/**
* <p>Formats a millisecond {@code long} value into the
* supplied {@code StringBuffer}.</p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 3.5
*/
@Override
public <B extends Appendable> B format(final long millis, final B buf) {
return printer.format(millis, buf);
}
/**
* <p>Formats a {@code Date} object into the
* supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 3.5
*/
@Override
public <B extends Appendable> B format(final Date date, final B buf) {
return printer.format(date, buf);
}
/**
* <p>Formats a {@code Calendar} object into the
* supplied {@code StringBuffer}.</p>
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 3.5
*/
@Override
public <B extends Appendable> B format(final Calendar calendar, final B buf) {
return printer.format(calendar, buf);
}
// Parsing
//-----------------------------------------------------------------------
/* (non-Javadoc)
* @see DateParser#parse(java.lang.String)
*/
@Override
public Date parse(final String source) throws ParseException {
return parser.parse(source);
}
/* (non-Javadoc)
* @see DateParser#parse(java.lang.String, java.text.ParsePosition)
*/
@Override
public Date parse(final String source, final ParsePosition pos) {
return parser.parse(source, pos);
}
/*
* (non-Javadoc)
* @see DateParser#parse(java.lang.String, java.text.ParsePosition, java.util.Calendar)
*/
@Override
public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) {
return parser.parse(source, pos, calendar);
}
/* (non-Javadoc)
* @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
*/
@Override
public Object parseObject(final String source, final ParsePosition pos) {
return parser.parseObject(source, pos);
}
// Accessors
//-----------------------------------------------------------------------
/**
* <p>Gets the pattern used by this formatter.</p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
@Override
public String getPattern() {
return printer.getPattern();
}
/**
* <p>Gets the time zone used by this formatter.</p>
*
* <p>This zone is always used for {@code Date} formatting. </p>
*
* @return the time zone
*/
@Override
public TimeZone getTimeZone() {
return printer.getTimeZone();
}
/**
* <p>Gets the locale used by this formatter.</p>
*
* @return the locale
*/
@Override
public Locale getLocale() {
return printer.getLocale();
}
/**
* <p>Gets an estimate for the maximum string length that the
* formatter will produce.</p>
*
* <p>The actual formatted length will almost always be less than or
* equal to this amount.</p>
*
* @return the maximum formatted length
*/
public int getMaxLengthEstimate() {
return printer.getMaxLengthEstimate();
}
// Basics
//-----------------------------------------------------------------------
/**
* <p>Compares two objects for equality.</p>
*
* @param obj the object to compare to
* @return {@code true} if equal
*/
@Override
public boolean equals(final Object obj) {
if (obj instanceof FastDateFormat == false) {
return false;
}
final FastDateFormat other = (FastDateFormat) obj;
// no need to check parser, as it has same invariants as printer
return printer.equals(other.printer);
}
/**
* <p>Returns a hash code compatible with equals.</p>
*
* @return a hash code compatible with equals
*/
@Override
public int hashCode() {
return printer.hashCode();
}
/**
* <p>Gets a debugging string version of this formatter.</p>
*
* @return a debugging string
*/
@Override
public String toString() {
return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]";
}
/**
* <p>Performs the formatting by applying the rules to the
* specified calendar.</p>
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {@link #format(Calendar, Appendable)}
*/
@Deprecated
protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) {
return printer.applyRules(calendar, buf);
}
}

View File

@ -1,997 +0,0 @@
/*
* Copyright (c) 2016, Liu Dong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.qihoo360.replugin.ext.lang3.time;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <p>FastDateParser is a fast and thread-safe version of
* {@link java.text.SimpleDateFormat}.</p>
*
* <p>To obtain a proxy to a FastDateParser, use {@link FastDateFormat#getInstance(String, TimeZone, Locale)}
* or another variation of the factory methods of {@link FastDateFormat}.</p>
*
* <p>Since FastDateParser is thread safe, you can use a static member instance:</p>
* <code>
* private static final DateParser DATE_PARSER = FastDateFormat.getInstance("yyyy-MM-dd");
* </code>
*
* <p>This class can be used as a direct replacement for
* <code>SimpleDateFormat</code> in most parsing situations.
* This class is especially useful in multi-threaded server environments.
* <code>SimpleDateFormat</code> is not thread-safe in any JDK version,
* nor will it be as Sun has closed the
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4228335">bug</a>/RFE.
* </p>
*
* <p>Only parsing is supported by this class, but all patterns are compatible with
* SimpleDateFormat.</p>
*
* <p>The class operates in lenient mode, so for example a time of 90 minutes is treated as 1 hour 30 minutes.</p>
*
* <p>Timing tests indicate this class is as about as fast as SimpleDateFormat
* in single thread applications and about 25% faster in multi-thread applications.</p>
*
* @since 3.2
* @see FastDatePrinter
*/
public class FastDateParser implements DateParser, Serializable {
/**
* Required for serialization support.
*
* @see Serializable
*/
private static final long serialVersionUID = 3L;
static final Locale JAPANESE_IMPERIAL = new Locale("ja","JP","JP");
// defining fields
private final String pattern;
private final TimeZone timeZone;
private final Locale locale;
private final int century;
private final int startYear;
// derived fields
private transient List<StrategyAndWidth> patterns;
// comparator used to sort regex alternatives
// alternatives should be ordered longer first, and shorter last. ('february' before 'feb')
// all entries must be lowercase by locale.
private static final Comparator<String> LONGER_FIRST_LOWERCASE = new Comparator<String>() {
@Override
public int compare(final String left, final String right) {
return right.compareTo(left);
}
};
/**
* <p>Constructs a new FastDateParser.</p>
*
* Use {@link FastDateFormat#getInstance(String, TimeZone, Locale)} or another variation of the
* factory methods of {@link FastDateFormat} to get a cached FastDateParser instance.
*
* @param pattern non-null {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale
*/
protected FastDateParser(final String pattern, final TimeZone timeZone, final Locale locale) {
this(pattern, timeZone, locale, null);
}
/**
* <p>Constructs a new FastDateParser.</p>
*
* @param pattern non-null {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale
* @param centuryStart The start of the century for 2 digit year parsing
*
* @since 3.5
*/
protected FastDateParser(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
this.pattern = pattern;
this.timeZone = timeZone;
this.locale = locale;
final Calendar definingCalendar = Calendar.getInstance(timeZone, locale);
int centuryStartYear;
if(centuryStart!=null) {
definingCalendar.setTime(centuryStart);
centuryStartYear= definingCalendar.get(Calendar.YEAR);
} else if(locale.equals(JAPANESE_IMPERIAL)) {
centuryStartYear= 0;
} else {
// from 80 years ago to 20 years from now
definingCalendar.setTime(new Date());
centuryStartYear= definingCalendar.get(Calendar.YEAR)-80;
}
century= centuryStartYear / 100 * 100;
startYear= centuryStartYear - century;
init(definingCalendar);
}
/**
* Initialize derived fields from defining fields.
* This is called from constructor and from readObject (de-serialization)
*
* @param definingCalendar the {@link Calendar} instance used to initialize this FastDateParser
*/
private void init(final Calendar definingCalendar) {
patterns = new ArrayList<>();
final StrategyParser fm = new StrategyParser(definingCalendar);
for(;;) {
final StrategyAndWidth field = fm.getNextStrategy();
if(field==null) {
break;
}
patterns.add(field);
}
}
// helper classes to parse the format string
//-----------------------------------------------------------------------
/**
* Holds strategy and field width
*/
private static class StrategyAndWidth {
final Strategy strategy;
final int width;
StrategyAndWidth(final Strategy strategy, final int width) {
this.strategy = strategy;
this.width = width;
}
int getMaxWidth(final ListIterator<StrategyAndWidth> lt) {
if(!strategy.isNumber() || !lt.hasNext()) {
return 0;
}
final Strategy nextStrategy = lt.next().strategy;
lt.previous();
return nextStrategy.isNumber() ?width :0;
}
}
/**
* Parse format into Strategies
*/
private class StrategyParser {
private final Calendar definingCalendar;
private int currentIdx;
StrategyParser(final Calendar definingCalendar) {
this.definingCalendar = definingCalendar;
}
StrategyAndWidth getNextStrategy() {
if (currentIdx >= pattern.length()) {
return null;
}
final char c = pattern.charAt(currentIdx);
if (isFormatLetter(c)) {
return letterPattern(c);
}
return literal();
}
private StrategyAndWidth letterPattern(final char c) {
final int begin = currentIdx;
while (++currentIdx < pattern.length()) {
if (pattern.charAt(currentIdx) != c) {
break;
}
}
final int width = currentIdx - begin;
return new StrategyAndWidth(getStrategy(c, width, definingCalendar), width);
}
private StrategyAndWidth literal() {
boolean activeQuote = false;
final StringBuilder sb = new StringBuilder();
while (currentIdx < pattern.length()) {
final char c = pattern.charAt(currentIdx);
if (!activeQuote && isFormatLetter(c)) {
break;
} else if (c == '\'' && (++currentIdx == pattern.length() || pattern.charAt(currentIdx) != '\'')) {
activeQuote = !activeQuote;
continue;
}
++currentIdx;
sb.append(c);
}
if (activeQuote) {
throw new IllegalArgumentException("Unterminated quote");
}
final String formatField = sb.toString();
return new StrategyAndWidth(new CopyQuotedStrategy(formatField), formatField.length());
}
}
private static boolean isFormatLetter(final char c) {
return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
}
// Accessors
//-----------------------------------------------------------------------
/* (non-Javadoc)
* @see DateParser#getPattern()
*/
@Override
public String getPattern() {
return pattern;
}
/* (non-Javadoc)
* @see DateParser#getTimeZone()
*/
@Override
public TimeZone getTimeZone() {
return timeZone;
}
/* (non-Javadoc)
* @see DateParser#getLocale()
*/
@Override
public Locale getLocale() {
return locale;
}
// Basics
//-----------------------------------------------------------------------
/**
* <p>Compare another object for equality with this object.</p>
*
* @param obj the object to compare to
* @return <code>true</code>if equal to this instance
*/
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof FastDateParser)) {
return false;
}
final FastDateParser other = (FastDateParser) obj;
return pattern.equals(other.pattern)
&& timeZone.equals(other.timeZone)
&& locale.equals(other.locale);
}
/**
* <p>Return a hash code compatible with equals.</p>
*
* @return a hash code compatible with equals
*/
@Override
public int hashCode() {
return pattern.hashCode() + 13 * (timeZone.hashCode() + 13 * locale.hashCode());
}
/**
* <p>Get a string version of this formatter.</p>
*
* @return a debugging string
*/
@Override
public String toString() {
return "FastDateParser[" + pattern + "," + locale + "," + timeZone.getID() + "]";
}
// Serializing
//-----------------------------------------------------------------------
/**
* Create the object after serialization. This implementation reinitializes the
* transient properties.
*
* @param in ObjectInputStream from which the object is being deserialized.
* @throws IOException if there is an IO issue.
* @throws ClassNotFoundException if a class cannot be found.
*/
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
final Calendar definingCalendar = Calendar.getInstance(timeZone, locale);
init(definingCalendar);
}
/* (non-Javadoc)
* @see DateParser#parseObject(java.lang.String)
*/
@Override
public Object parseObject(final String source) throws ParseException {
return parse(source);
}
/* (non-Javadoc)
* @see DateParser#parse(java.lang.String)
*/
@Override
public Date parse(final String source) throws ParseException {
final ParsePosition pp = new ParsePosition(0);
final Date date= parse(source, pp);
if (date == null) {
// Add a note re supported date range
if (locale.equals(JAPANESE_IMPERIAL)) {
throw new ParseException(
"(The " +locale + " locale does not support dates before 1868 AD)\n" +
"Unparseable date: \""+source, pp.getErrorIndex());
}
throw new ParseException("Unparseable date: "+source, pp.getErrorIndex());
}
return date;
}
/* (non-Javadoc)
* @see DateParser#parseObject(java.lang.String, java.text.ParsePosition)
*/
@Override
public Object parseObject(final String source, final ParsePosition pos) {
return parse(source, pos);
}
/**
* This implementation updates the ParsePosition if the parse succeeds.
* However, it sets the error index to the position before the failed field unlike
* the method {@link java.text.SimpleDateFormat#parse(String, ParsePosition)} which sets
* the error index to after the failed field.
* <p>
* To determine if the parse has succeeded, the caller must check if the current parse position
* given by {@link ParsePosition#getIndex()} has been updated. If the input buffer has been fully
* parsed, then the index will point to just after the end of the input buffer.
*
* @see DateParser#parse(String, ParsePosition)
*/
@Override
public Date parse(final String source, final ParsePosition pos) {
// timing tests indicate getting new instance is 19% faster than cloning
final Calendar cal= Calendar.getInstance(timeZone, locale);
cal.clear();
return parse(source, pos, cal) ? cal.getTime() : null;
}
/**
* Parse a formatted date string according to the format. Updates the Calendar with parsed fields.
* Upon success, the ParsePosition index is updated to indicate how much of the source text was consumed.
* Not all source text needs to be consumed. Upon parse failure, ParsePosition error index is updated to
* the offset of the source text which does not match the supplied format.
*
* @param source The text to parse.
* @param pos On input, the position in the source to start parsing, on output, updated position.
* @param calendar The calendar into which to set parsed fields.
* @return true, if source has been parsed (pos parsePosition is updated); otherwise false (and pos errorIndex is updated)
* @throws IllegalArgumentException when Calendar has been set to be not lenient, and a parsed field is
* out of range.
*/
@Override
public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) {
final ListIterator<StrategyAndWidth> lt = patterns.listIterator();
while (lt.hasNext()) {
final StrategyAndWidth strategyAndWidth = lt.next();
final int maxWidth = strategyAndWidth.getMaxWidth(lt);
if (!strategyAndWidth.strategy.parse(this, calendar, source, pos, maxWidth)) {
return false;
}
}
return true;
}
// Support for strategies
//-----------------------------------------------------------------------
private static StringBuilder simpleQuote(final StringBuilder sb, final String value) {
for (int i = 0; i < value.length(); ++i) {
final char c = value.charAt(i);
switch (c) {
case '\\':
case '^':
case '$':
case '.':
case '|':
case '?':
case '*':
case '+':
case '(':
case ')':
case '[':
case '{':
sb.append('\\');
default:
sb.append(c);
}
}
return sb;
}
/**
* Get the short and long values displayed for a field
* @param cal The calendar to obtain the short and long values
* @param locale The locale of display names
* @param field The field of interest
* @param regex The regular expression to build
* @return The map of string display names to field values
*/
private static Map<String, Integer> appendDisplayNames(final Calendar cal, final Locale locale, final int field, final StringBuilder regex) {
final Map<String, Integer> values = new HashMap<>();
final Map<String, Integer> displayNames = cal.getDisplayNames(field, Calendar.ALL_STYLES, locale);
final TreeSet<String> sorted = new TreeSet<>(LONGER_FIRST_LOWERCASE);
for (final Map.Entry<String, Integer> displayName : displayNames.entrySet()) {
final String key = displayName.getKey().toLowerCase(locale);
if (sorted.add(key)) {
values.put(key, displayName.getValue());
}
}
for (final String symbol : sorted) {
simpleQuote(regex, symbol).append('|');
}
return values;
}
/**
* Adjust dates to be within appropriate century
* @param twoDigitYear The year to adjust
* @return A value between centuryStart(inclusive) to centuryStart+100(exclusive)
*/
private int adjustYear(final int twoDigitYear) {
final int trial = century + twoDigitYear;
return twoDigitYear >= startYear ? trial : trial + 100;
}
/**
* A strategy to parse a single field from the parsing pattern
*/
private abstract static class Strategy {
/**
* Is this field a number?
* The default implementation returns false.
*
* @return true, if field is a number
*/
boolean isNumber() {
return false;
}
abstract boolean parse(FastDateParser parser, Calendar calendar, String source, ParsePosition pos, int maxWidth);
}
/**
* A strategy to parse a single field from the parsing pattern
*/
private abstract static class PatternStrategy extends Strategy {
private Pattern pattern;
void createPattern(final StringBuilder regex) {
createPattern(regex.toString());
}
void createPattern(final String regex) {
this.pattern = Pattern.compile(regex);
}
/**
* Is this field a number?
* The default implementation returns false.
*
* @return true, if field is a number
*/
@Override
boolean isNumber() {
return false;
}
@Override
boolean parse(final FastDateParser parser, final Calendar calendar, final String source, final ParsePosition pos, final int maxWidth) {
final Matcher matcher = pattern.matcher(source.substring(pos.getIndex()));
if (!matcher.lookingAt()) {
pos.setErrorIndex(pos.getIndex());
return false;
}
pos.setIndex(pos.getIndex() + matcher.end(1));
setCalendar(parser, calendar, matcher.group(1));
return true;
}
abstract void setCalendar(FastDateParser parser, Calendar cal, String value);
}
/**
* Obtain a Strategy given a field from a SimpleDateFormat pattern
* @param f A sub-sequence of the SimpleDateFormat pattern
* @param definingCalendar The calendar to obtain the short and long values
* @return The Strategy that will handle parsing for the field
*/
private Strategy getStrategy(final char f, final int width, final Calendar definingCalendar) {
switch(f) {
default:
throw new IllegalArgumentException("Format '"+f+"' not supported");
case 'D':
return DAY_OF_YEAR_STRATEGY;
case 'E':
return getLocaleSpecificStrategy(Calendar.DAY_OF_WEEK, definingCalendar);
case 'F':
return DAY_OF_WEEK_IN_MONTH_STRATEGY;
case 'G':
return getLocaleSpecificStrategy(Calendar.ERA, definingCalendar);
case 'H': // Hour in day (0-23)
return HOUR_OF_DAY_STRATEGY;
case 'K': // Hour in am/pm (0-11)
return HOUR_STRATEGY;
case 'M':
return width>=3 ?getLocaleSpecificStrategy(Calendar.MONTH, definingCalendar) :NUMBER_MONTH_STRATEGY;
case 'S':
return MILLISECOND_STRATEGY;
case 'W':
return WEEK_OF_MONTH_STRATEGY;
case 'a':
return getLocaleSpecificStrategy(Calendar.AM_PM, definingCalendar);
case 'd':
return DAY_OF_MONTH_STRATEGY;
case 'h': // Hour in am/pm (1-12), i.e. midday/midnight is 12, not 0
return HOUR12_STRATEGY;
case 'k': // Hour in day (1-24), i.e. midnight is 24, not 0
return HOUR24_OF_DAY_STRATEGY;
case 'm':
return MINUTE_STRATEGY;
case 's':
return SECOND_STRATEGY;
case 'u':
return DAY_OF_WEEK_STRATEGY;
case 'w':
return WEEK_OF_YEAR_STRATEGY;
case 'y':
case 'Y':
return width>2 ?LITERAL_YEAR_STRATEGY :ABBREVIATED_YEAR_STRATEGY;
case 'X':
return ISO8601TimeZoneStrategy.getStrategy(width);
case 'Z':
if (width==2) {
return ISO8601TimeZoneStrategy.ISO_8601_3_STRATEGY;
}
//$FALL-THROUGH$
case 'z':
return getLocaleSpecificStrategy(Calendar.ZONE_OFFSET, definingCalendar);
}
}
@SuppressWarnings("unchecked") // OK because we are creating an array with no entries
private static final ConcurrentMap<Locale, Strategy>[] caches = new ConcurrentMap[Calendar.FIELD_COUNT];
/**
* Get a cache of Strategies for a particular field
* @param field The Calendar field
* @return a cache of Locale to Strategy
*/
private static ConcurrentMap<Locale, Strategy> getCache(final int field) {
synchronized (caches) {
if (caches[field] == null) {
caches[field] = new ConcurrentHashMap<>(3);
}
return caches[field];
}
}
/**
* Construct a Strategy that parses a Text field
* @param field The Calendar field
* @param definingCalendar The calendar to obtain the short and long values
* @return a TextStrategy for the field and Locale
*/
private Strategy getLocaleSpecificStrategy(final int field, final Calendar definingCalendar) {
final ConcurrentMap<Locale, Strategy> cache = getCache(field);
Strategy strategy = cache.get(locale);
if (strategy == null) {
strategy = field == Calendar.ZONE_OFFSET
? new TimeZoneStrategy(locale)
: new CaseInsensitiveTextStrategy(field, definingCalendar, locale);
final Strategy inCache = cache.putIfAbsent(locale, strategy);
if (inCache != null) {
return inCache;
}
}
return strategy;
}
/**
* A strategy that copies the static or quoted field in the parsing pattern
*/
private static class CopyQuotedStrategy extends Strategy {
private final String formatField;
/**
* Construct a Strategy that ensures the formatField has literal text
* @param formatField The literal text to match
*/
CopyQuotedStrategy(final String formatField) {
this.formatField = formatField;
}
/**
* {@inheritDoc}
*/
@Override
boolean isNumber() {
return false;
}
@Override
boolean parse(final FastDateParser parser, final Calendar calendar, final String source, final ParsePosition pos, final int maxWidth) {
for (int idx = 0; idx < formatField.length(); ++idx) {
final int sIdx = idx + pos.getIndex();
if (sIdx == source.length()) {
pos.setErrorIndex(sIdx);
return false;
}
if (formatField.charAt(idx) != source.charAt(sIdx)) {
pos.setErrorIndex(sIdx);
return false;
}
}
pos.setIndex(formatField.length() + pos.getIndex());
return true;
}
}
/**
* A strategy that handles a text field in the parsing pattern
*/
private static class CaseInsensitiveTextStrategy extends PatternStrategy {
private final int field;
final Locale locale;
private final Map<String, Integer> lKeyValues;
/**
* Construct a Strategy that parses a Text field
* @param field The Calendar field
* @param definingCalendar The Calendar to use
* @param locale The Locale to use
*/
CaseInsensitiveTextStrategy(final int field, final Calendar definingCalendar, final Locale locale) {
this.field = field;
this.locale = locale;
final StringBuilder regex = new StringBuilder();
regex.append("((?iu)");
lKeyValues = appendDisplayNames(definingCalendar, locale, field, regex);
regex.setLength(regex.length()-1);
regex.append(")");
createPattern(regex);
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
final Integer iVal = lKeyValues.get(value.toLowerCase(locale));
cal.set(field, iVal.intValue());
}
}
/**
* A strategy that handles a number field in the parsing pattern
*/
private static class NumberStrategy extends Strategy {
private final int field;
/**
* Construct a Strategy that parses a Number field
* @param field The Calendar field
*/
NumberStrategy(final int field) {
this.field= field;
}
/**
* {@inheritDoc}
*/
@Override
boolean isNumber() {
return true;
}
@Override
boolean parse(final FastDateParser parser, final Calendar calendar, final String source, final ParsePosition pos, final int maxWidth) {
int idx = pos.getIndex();
int last = source.length();
if (maxWidth == 0) {
// if no maxWidth, strip leading white space
for (; idx < last; ++idx) {
final char c = source.charAt(idx);
if (!Character.isWhitespace(c)) {
break;
}
}
pos.setIndex(idx);
} else {
final int end = idx + maxWidth;
if (last > end) {
last = end;
}
}
for (; idx < last; ++idx) {
final char c = source.charAt(idx);
if (!Character.isDigit(c)) {
break;
}
}
if (pos.getIndex() == idx) {
pos.setErrorIndex(idx);
return false;
}
final int value = Integer.parseInt(source.substring(pos.getIndex(), idx));
pos.setIndex(idx);
calendar.set(field, modify(parser, value));
return true;
}
/**
* Make any modifications to parsed integer
* @param parser The parser
* @param iValue The parsed integer
* @return The modified value
*/
int modify(final FastDateParser parser, final int iValue) {
return iValue;
}
}
private static final Strategy ABBREVIATED_YEAR_STRATEGY = new NumberStrategy(Calendar.YEAR) {
/**
* {@inheritDoc}
*/
@Override
int modify(final FastDateParser parser, final int iValue) {
return iValue < 100 ? parser.adjustYear(iValue) : iValue;
}
};
/**
* A strategy that handles a timezone field in the parsing pattern
*/
static class TimeZoneStrategy extends PatternStrategy {
private static final String RFC_822_TIME_ZONE = "[+-]\\d{4}";
private static final String GMT_OPTION= "GMT[+-]\\d{1,2}:\\d{2}";
private final Locale locale;
private final Map<String, TzInfo> tzNames= new HashMap<>();
private static class TzInfo {
TimeZone zone;
int dstOffset;
TzInfo(final TimeZone tz, final boolean useDst) {
zone = tz;
dstOffset = useDst ?tz.getDSTSavings() :0;
}
}
/**
* Index of zone id
*/
private static final int ID = 0;
/**
* Construct a Strategy that parses a TimeZone
* @param locale The Locale
*/
TimeZoneStrategy(final Locale locale) {
this.locale = locale;
final StringBuilder sb = new StringBuilder();
sb.append("((?iu)" + RFC_822_TIME_ZONE + "|" + GMT_OPTION );
final Set<String> sorted = new TreeSet<>(LONGER_FIRST_LOWERCASE);
final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings();
for (final String[] zoneNames : zones) {
// offset 0 is the time zone ID and is not localized
final String tzId = zoneNames[ID];
if (tzId.equalsIgnoreCase("GMT")) {
continue;
}
final TimeZone tz = TimeZone.getTimeZone(tzId);
// offset 1 is long standard name
// offset 2 is short standard name
final TzInfo standard = new TzInfo(tz, false);
TzInfo tzInfo = standard;
for (int i = 1; i < zoneNames.length; ++i) {
switch (i) {
case 3: // offset 3 is long daylight savings (or summertime) name
// offset 4 is the short summertime name
tzInfo = new TzInfo(tz, true);
break;
case 5: // offset 5 starts additional names, probably standard time
tzInfo = standard;
break;
default:
break;
}
if (zoneNames[i] != null) {
final String key = zoneNames[i].toLowerCase(locale);
// ignore the data associated with duplicates supplied in
// the additional names
if (sorted.add(key)) {
tzNames.put(key, tzInfo);
}
}
}
}
// order the regex alternatives with longer strings first, greedy
// match will ensure longest string will be consumed
for (final String zoneName : sorted) {
simpleQuote(sb.append('|'), zoneName);
}
sb.append(")");
createPattern(sb);
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
if (value.charAt(0) == '+' || value.charAt(0) == '-') {
final TimeZone tz = TimeZone.getTimeZone("GMT" + value);
cal.setTimeZone(tz);
} else if (value.regionMatches(true, 0, "GMT", 0, 3)) {
final TimeZone tz = TimeZone.getTimeZone(value.toUpperCase());
cal.setTimeZone(tz);
} else {
final TzInfo tzInfo = tzNames.get(value.toLowerCase(locale));
cal.set(Calendar.DST_OFFSET, tzInfo.dstOffset);
cal.set(Calendar.ZONE_OFFSET, tzInfo.zone.getRawOffset());
}
}
}
private static class ISO8601TimeZoneStrategy extends PatternStrategy {
// Z, +hh, -hh, +hhmm, -hhmm, +hh:mm or -hh:mm
/**
* Construct a Strategy that parses a TimeZone
* @param pattern The Pattern
*/
ISO8601TimeZoneStrategy(final String pattern) {
createPattern(pattern);
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
if (value.equals("Z")) {
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
} else {
cal.setTimeZone(TimeZone.getTimeZone("GMT" + value));
}
}
private static final Strategy ISO_8601_1_STRATEGY = new ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}))");
private static final Strategy ISO_8601_2_STRATEGY = new ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}\\d{2}))");
private static final Strategy ISO_8601_3_STRATEGY = new ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}(?::)\\d{2}))");
/**
* Factory method for ISO8601TimeZoneStrategies.
*
* @param tokenLen a token indicating the length of the TimeZone String to be formatted.
* @return a ISO8601TimeZoneStrategy that can format TimeZone String of length {@code tokenLen}. If no such
* strategy exists, an IllegalArgumentException will be thrown.
*/
static Strategy getStrategy(final int tokenLen) {
switch(tokenLen) {
case 1:
return ISO_8601_1_STRATEGY;
case 2:
return ISO_8601_2_STRATEGY;
case 3:
return ISO_8601_3_STRATEGY;
default:
throw new IllegalArgumentException("invalid number of X");
}
}
}
private static final Strategy NUMBER_MONTH_STRATEGY = new NumberStrategy(Calendar.MONTH) {
@Override
int modify(final FastDateParser parser, final int iValue) {
return iValue-1;
}
};
private static final Strategy LITERAL_YEAR_STRATEGY = new NumberStrategy(Calendar.YEAR);
private static final Strategy WEEK_OF_YEAR_STRATEGY = new NumberStrategy(Calendar.WEEK_OF_YEAR);
private static final Strategy WEEK_OF_MONTH_STRATEGY = new NumberStrategy(Calendar.WEEK_OF_MONTH);
private static final Strategy DAY_OF_YEAR_STRATEGY = new NumberStrategy(Calendar.DAY_OF_YEAR);
private static final Strategy DAY_OF_MONTH_STRATEGY = new NumberStrategy(Calendar.DAY_OF_MONTH);
private static final Strategy DAY_OF_WEEK_STRATEGY = new NumberStrategy(Calendar.DAY_OF_WEEK) {
@Override
int modify(final FastDateParser parser, final int iValue) {
return iValue != 7 ? iValue + 1 : Calendar.SUNDAY;
}
};
private static final Strategy DAY_OF_WEEK_IN_MONTH_STRATEGY = new NumberStrategy(Calendar.DAY_OF_WEEK_IN_MONTH);
private static final Strategy HOUR_OF_DAY_STRATEGY = new NumberStrategy(Calendar.HOUR_OF_DAY);
private static final Strategy HOUR24_OF_DAY_STRATEGY = new NumberStrategy(Calendar.HOUR_OF_DAY) {
@Override
int modify(final FastDateParser parser, final int iValue) {
return iValue == 24 ? 0 : iValue;
}
};
private static final Strategy HOUR12_STRATEGY = new NumberStrategy(Calendar.HOUR) {
@Override
int modify(final FastDateParser parser, final int iValue) {
return iValue == 12 ? 0 : iValue;
}
};
private static final Strategy HOUR_STRATEGY = new NumberStrategy(Calendar.HOUR);
private static final Strategy MINUTE_STRATEGY = new NumberStrategy(Calendar.MINUTE);
private static final Strategy SECOND_STRATEGY = new NumberStrategy(Calendar.SECOND);
private static final Strategy MILLISECOND_STRATEGY = new NumberStrategy(Calendar.MILLISECOND);
}

View File

@ -1,273 +0,0 @@
/*
* Copyright (c) 2016, Liu Dong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.qihoo360.replugin.ext.lang3.time;
import java.text.DateFormat;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.qihoo360.replugin.ext.lang3.Validate;
/**
* <p>FormatCache is a cache and factory for {@link Format}s.</p>
*
* @since 3.0
*/
// TODO: Before making public move from getDateTimeInstance(Integer,...) to int; or some other approach.
abstract class FormatCache<F extends Format> {
/**
* No date or no time. Used in same parameters as DateFormat.SHORT or DateFormat.LONG
*/
static final int NONE= -1;
private final ConcurrentMap<MultipartKey, F> cInstanceCache
= new ConcurrentHashMap<>(7);
private static final ConcurrentMap<MultipartKey, String> cDateTimeInstanceCache
= new ConcurrentHashMap<>(7);
/**
* <p>Gets a formatter instance using the default pattern in the
* default timezone and locale.</p>
*
* @return a date/time formatter
*/
public F getInstance() {
return getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, TimeZone.getDefault(), Locale.getDefault());
}
/**
* <p>Gets a formatter instance using the specified pattern, time zone
* and locale.</p>
*
* @param pattern {@link SimpleDateFormat} compatible
* pattern, non-null
* @param timeZone the time zone, null means use the default TimeZone
* @param locale the locale, null means use the default Locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or <code>null</code>
*/
public F getInstance(final String pattern, TimeZone timeZone, Locale locale) {
Validate.notNull(pattern, "pattern must not be null");
if (timeZone == null) {
timeZone = TimeZone.getDefault();
}
if (locale == null) {
locale = Locale.getDefault();
}
final MultipartKey key = new MultipartKey(pattern, timeZone, locale);
F format = cInstanceCache.get(key);
if (format == null) {
format = createInstance(pattern, timeZone, locale);
final F previousValue= cInstanceCache.putIfAbsent(key, format);
if (previousValue != null) {
// another thread snuck in and did the same work
// we should return the instance that is in ConcurrentMap
format= previousValue;
}
}
return format;
}
/**
* <p>Create a format instance using the specified pattern, time zone
* and locale.</p>
*
* @param pattern {@link SimpleDateFormat} compatible pattern, this will not be null.
* @param timeZone time zone, this will not be null.
* @param locale locale, this will not be null.
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or <code>null</code>
*/
protected abstract F createInstance(String pattern, TimeZone timeZone, Locale locale);
/**
* <p>Gets a date/time formatter instance using the specified style,
* time zone and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
*/
// This must remain private, see LANG-884
private F getDateTimeInstance(final Integer dateStyle, final Integer timeStyle, final TimeZone timeZone, Locale locale) {
if (locale == null) {
locale = Locale.getDefault();
}
final String pattern = getPatternForStyle(dateStyle, timeStyle, locale);
return getInstance(pattern, timeZone, locale);
}
/**
* <p>Gets a date/time formatter instance using the specified style,
* time zone and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* <p>Gets a date formatter instance using the specified style,
* time zone and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateInstance(final int dateStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), null, timeZone, locale);
}
/**
* <p>Gets a time formatter instance using the specified style,
* time zone and locale.</p>
*
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getTimeInstance(final int timeStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(null, Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* <p>Gets a date/time format for the specified styles and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param locale The non-null locale of the desired format
* @return a localized standard date/time format
* @throws IllegalArgumentException if the Locale has no date/time pattern defined
*/
// package protected, for access from test code; do not make public or protected
static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) {
final MultipartKey key = new MultipartKey(dateStyle, timeStyle, locale);
String pattern = cDateTimeInstanceCache.get(key);
if (pattern == null) {
try {
DateFormat formatter;
if (dateStyle == null) {
formatter = DateFormat.getTimeInstance(timeStyle.intValue(), locale);
} else if (timeStyle == null) {
formatter = DateFormat.getDateInstance(dateStyle.intValue(), locale);
} else {
formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), locale);
}
pattern = ((SimpleDateFormat)formatter).toPattern();
final String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern);
if (previous != null) {
// even though it doesn't matter if another thread put the pattern
// it's still good practice to return the String instance that is
// actually in the ConcurrentMap
pattern= previous;
}
} catch (final ClassCastException ex) {
throw new IllegalArgumentException("No date time pattern for locale: " + locale);
}
}
return pattern;
}
// ----------------------------------------------------------------------
/**
* <p>Helper class to hold multi-part Map keys</p>
*/
private static class MultipartKey {
private final Object[] keys;
private int hashCode;
/**
* Constructs an instance of <code>MultipartKey</code> to hold the specified objects.
* @param keys the set of objects that make up the key. Each key may be null.
*/
MultipartKey(final Object... keys) {
this.keys = keys;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(final Object obj) {
// Eliminate the usual boilerplate because
// this inner static class is only used in a generic ConcurrentHashMap
// which will not compare against other Object types
return Arrays.equals(keys, ((MultipartKey)obj).keys);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
if(hashCode==0) {
int rc= 0;
for(final Object key : keys) {
if(key!=null) {
rc= rc*7 + key.hashCode();
}
}
hashCode= rc;
}
return hashCode;
}
}
}

View File

@ -1,123 +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 com.qihoo360.replugin.ext.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 {@code final}, so a subclass
* can not add undesirable behaviour.</p>
*
* <p>#ThreadSafe# if both paired objects are thread-safe</p>
*
* @param <L> the left element type
* @param <R> the right element type
*
* @since Lang 3.0
*/
public final class ImmutablePair<L, R> extends Pair<L, R> {
/**
* An immutable pair of nulls.
*/
// This is not defined with generics to avoid warnings in call sites.
@SuppressWarnings("rawtypes")
private static final ImmutablePair NULL = ImmutablePair.of(null, null);
/** Serialization version */
private static final long serialVersionUID = 4954918890077093841L;
/**
* Returns an immutable pair of nulls.
*
* @param <L> the left element of this pair. Value is {@code null}.
* @param <R> the right element of this pair. Value is {@code null}.
* @return an immutable pair of nulls.
* @since 3.6
*/
@SuppressWarnings("unchecked")
public static <L, R> ImmutablePair<L, R> nullPair() {
return NULL;
}
/** 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(final L left, final R right) {
return new ImmutablePair<>(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(final L left, final 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
*/
@Override
public R setValue(final R value) {
throw new UnsupportedOperationException();
}
}

View File

@ -1,125 +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 com.qihoo360.replugin.ext.lang3.tuple;
/**
* <p>An immutable triple consisting of three {@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 triple, then the triple
* itself effectively becomes mutable. The class is also {@code final}, so a subclass
* can not add undesirable behaviour.</p>
*
* <p>#ThreadSafe# if all three objects are thread-safe</p>
*
* @param <L> the left element type
* @param <M> the middle element type
* @param <R> the right element type
*
* @since 3.2
*/
public final class ImmutableTriple<L, M, R> extends Triple<L, M, R> {
/**
* An immutable triple of nulls.
*/
// This is not defined with generics to avoid warnings in call sites.
@SuppressWarnings("rawtypes")
private static final ImmutableTriple NULL = ImmutableTriple.of(null, null, null);
/** Serialization version */
private static final long serialVersionUID = 1L;
/**
* Returns an immutable triple of nulls.
*
* @param <L> the left element of this triple. Value is {@code null}.
* @param <M> the middle element of this triple. Value is {@code null}.
* @param <R> the right element of this triple. Value is {@code null}.
* @return an immutable triple of nulls.
* @since 3.6
*/
@SuppressWarnings("unchecked")
public static <L, M, R> ImmutableTriple<L, M, R> nullTriple() {
return NULL;
}
/** Left object */
public final L left;
/** Middle object */
public final M middle;
/** Right object */
public final R right;
/**
* <p>Obtains an immutable triple of from three objects inferring the generic types.</p>
*
* <p>This factory allows the triple to be created using inference to
* obtain the generic types.</p>
*
* @param <L> the left element type
* @param <M> the middle element type
* @param <R> the right element type
* @param left the left element, may be null
* @param middle the middle element, may be null
* @param right the right element, may be null
* @return a triple formed from the three parameters, not null
*/
public static <L, M, R> ImmutableTriple<L, M, R> of(final L left, final M middle, final R right) {
return new ImmutableTriple<>(left, middle, right);
}
/**
* Create a new triple instance.
*
* @param left the left value, may be null
* @param middle the middle value, may be null
* @param right the right value, may be null
*/
public ImmutableTriple(final L left, final M middle, final R right) {
super();
this.left = left;
this.middle = middle;
this.right = right;
}
//-----------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public L getLeft() {
return left;
}
/**
* {@inheritDoc}
*/
@Override
public M getMiddle() {
return middle;
}
/**
* {@inheritDoc}
*/
@Override
public R getRight() {
return right;
}
}

View File

@ -1,178 +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 com.qihoo360.replugin.ext.lang3.tuple;
import com.qihoo360.replugin.ext.lang3.builder.CompareToBuilder;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
/**
* <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
*/
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(final L left, final R right) {
return new ImmutablePair<>(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
*/
@Override
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
*/
@Override
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
*/
@Override
public int compareTo(final 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(final Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Map.Entry<?, ?>) {
final Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj;
return Objects.equals(getKey(), other.getKey())
&& Objects.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(final String format) {
return String.format(format, getLeft(), getRight());
}
}

View File

@ -1,159 +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 com.qihoo360.replugin.ext.lang3.tuple;
import java.io.Serializable;
import java.util.Objects;
import com.qihoo360.replugin.ext.lang3.builder.CompareToBuilder;
/**
* <p>A triple consisting of three elements.</p>
*
* <p>This class is an abstract implementation defining the basic API.
* It refers to the elements as 'left', 'middle' and '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 triple, then the triple itself effectively becomes mutable.</p>
*
* @param <L> the left element type
* @param <M> the middle element type
* @param <R> the right element type
*
* @since 3.2
*/
public abstract class Triple<L, M, R> implements Comparable<Triple<L, M, R>>, Serializable {
/** Serialization version */
private static final long serialVersionUID = 1L;
/**
* <p>Obtains an immutable triple of from three objects inferring the generic types.</p>
*
* <p>This factory allows the triple to be created using inference to
* obtain the generic types.</p>
*
* @param <L> the left element type
* @param <M> the middle element type
* @param <R> the right element type
* @param left the left element, may be null
* @param middle the middle element, may be null
* @param right the right element, may be null
* @return a triple formed from the three parameters, not null
*/
public static <L, M, R> Triple<L, M, R> of(final L left, final M middle, final R right) {
return new ImmutableTriple<>(left, middle, right);
}
//-----------------------------------------------------------------------
/**
* <p>Gets the left element from this triple.</p>
*
* @return the left element, may be null
*/
public abstract L getLeft();
/**
* <p>Gets the middle element from this triple.</p>
*
* @return the middle element, may be null
*/
public abstract M getMiddle();
/**
* <p>Gets the right element from this triple.</p>
*
* @return the right element, may be null
*/
public abstract R getRight();
//-----------------------------------------------------------------------
/**
* <p>Compares the triple based on the left element, followed by the middle element,
* finally the right element.
* The types must be {@code Comparable}.</p>
*
* @param other the other triple, not null
* @return negative if this is less, zero if equal, positive if greater
*/
@Override
public int compareTo(final Triple<L, M, R> other) {
return new CompareToBuilder().append(getLeft(), other.getLeft())
.append(getMiddle(), other.getMiddle())
.append(getRight(), other.getRight()).toComparison();
}
/**
* <p>Compares this triple to another based on the three elements.</p>
*
* @param obj the object to compare to, null returns false
* @return true if the elements of the triple are equal
*/
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Triple<?, ?, ?>) {
final Triple<?, ?, ?> other = (Triple<?, ?, ?>) obj;
return Objects.equals(getLeft(), other.getLeft())
&& Objects.equals(getMiddle(), other.getMiddle())
&& Objects.equals(getRight(), other.getRight());
}
return false;
}
/**
* <p>Returns a suitable hash code.</p>
*
* @return the hash code
*/
@Override
public int hashCode() {
return (getLeft() == null ? 0 : getLeft().hashCode()) ^
(getMiddle() == null ? 0 : getMiddle().hashCode()) ^
(getRight() == null ? 0 : getRight().hashCode());
}
/**
* <p>Returns a String representation of this triple using the format {@code ($left,$middle,$right)}.</p>
*
* @return a string describing this object, not null
*/
@Override
public String toString() {
return "(" + getLeft() + "," + getMiddle() + "," + getRight() + ")";
}
/**
* <p>Formats the receiver using the given format.</p>
*
* <p>This uses {@link java.util.Formattable} to perform the formatting. Three variables may
* be used to embed the left and right elements. Use {@code %1$s} for the left
* element, {@code %2$s} for the middle and {@code %3$s} for the right element.
* The default format used by {@code toString()} is {@code (%1$s,%2$s,%3$s)}.</p>
*
* @param format the format string, optionally containing {@code %1$s}, {@code %2$s} and {@code %3$s}, not null
* @return the formatted string, not null
*/
public String toString(final String format) {
return String.format(format, getLeft(), getMiddle(), getRight());
}
}

View File

@ -16,8 +16,7 @@
package com.qihoo360.replugin.helper;
import com.qihoo360.replugin.ext.lang3.ClassUtils;
import com.qihoo360.replugin.ext.lang3.reflect.FieldUtils;
import com.qihoo360.replugin.utils.ReflectUtils;
/**
* 从宿主的 RePluginHostConfig 中获取一些字段值
@ -69,89 +68,89 @@ public class HostConfigHelper {
static {
try {
HOST_CONFIG_CLASS = ClassUtils.getClass(HOST_CONFIG_FILE_PATH + HOST_CONFIG_FILE_NAME);
HOST_CONFIG_CLASS = ReflectUtils.getClass(HOST_CONFIG_FILE_PATH + HOST_CONFIG_FILE_NAME);
} catch (ClassNotFoundException e) {
e.printStackTrace();
// Ignore, Just use default value
}
try {
ACTIVITY_PIT_USE_APPCOMPAT = readField("ACTIVITY_PIT_USE_APPCOMPAT");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
try {
ACTIVITY_PIT_COUNT_TS_STANDARD = readField("ACTIVITY_PIT_COUNT_TS_STANDARD");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
try {
ACTIVITY_PIT_COUNT_TS_SINGLE_TOP = readField("ACTIVITY_PIT_COUNT_TS_SINGLE_TOP");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
try {
ACTIVITY_PIT_COUNT_TS_SINGLE_TASK = readField("ACTIVITY_PIT_COUNT_TS_SINGLE_TASK");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
try {
ACTIVITY_PIT_COUNT_TS_SINGLE_INSTANCE = readField("ACTIVITY_PIT_COUNT_TS_SINGLE_INSTANCE");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
try {
ACTIVITY_PIT_COUNT_NTS_STANDARD = readField("ACTIVITY_PIT_COUNT_NTS_STANDARD");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
try {
ACTIVITY_PIT_COUNT_NTS_SINGLE_TOP = readField("ACTIVITY_PIT_COUNT_NTS_SINGLE_TOP");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
try {
ACTIVITY_PIT_COUNT_NTS_SINGLE_TASK = readField("ACTIVITY_PIT_COUNT_NTS_SINGLE_TASK");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
try {
ACTIVITY_PIT_COUNT_NTS_SINGLE_INSTANCE = readField("ACTIVITY_PIT_COUNT_NTS_SINGLE_INSTANCE");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
try {
ACTIVITY_PIT_COUNT_TASK = readField("ACTIVITY_PIT_COUNT_TASK");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
try {
ADAPTER_COMPATIBLE_VERSION = readField("ADAPTER_COMPATIBLE_VERSION");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
try {
ADAPTER_CURRENT_VERSION = readField("ADAPTER_CURRENT_VERSION");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// Ignore, Just use default value
}
}
private static <T> T readField(String name) {
private static <T> T readField(String name) throws NoSuchFieldException {
try {
// 就是要强转
//noinspection unchecked
return (T) FieldUtils.readStaticField(HOST_CONFIG_CLASS, name, true);
return (T) ReflectUtils.readStaticField(HOST_CONFIG_CLASS, name);
} catch (IllegalAccessException e) {
// 此Field可能为非Public权限不过由于我们做了Accessible处理可能性非常低
// NOTE 因为类型转换发生在readField返回值之后才做ClassCastException只会出现在static方法块内
@ -159,7 +158,7 @@ public class HostConfigHelper {
throw new IllegalStateException(e);
}
// NOTE 不需要Catch IllegalArgumentException因为只要此Field找不到就抛符合预期
// NOTE 不需要Catch NoSuchFieldException因为只要此Field找不到就抛符合预期
}
public static void init() {