/**
* Copyright 2005-2011 Noelios Technologies.
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: LGPL 3.0 or LGPL 2.1 or CDDL 1.0 or EPL 1.0 (the
* "Licenses"). You can select the license that you prefer but you may not use
* this file except in compliance with one of these Licenses.
*
* You can obtain a copy of the LGPL 3.0 license at
* http://www.opensource.org/licenses/lgpl-3.0.html
*
* You can obtain a copy of the LGPL 2.1 license at
* http://www.opensource.org/licenses/lgpl-2.1.php
*
* You can obtain a copy of the CDDL 1.0 license at
* http://www.opensource.org/licenses/cddl1.php
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://www.noelios.com/products/restlet-engine
*
* Restlet is a registered trademark of Noelios Technologies.
*/
package org.restlet.ext.jaxrs.internal.util;
import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
import static org.restlet.data.CharacterSet.UTF_8;
import java.io.IOException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.nio.charset.Charset;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.ws.rs.Path;
import javax.ws.rs.core.MultivaluedMap;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.CharacterSet;
import org.restlet.data.Dimension;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Metadata;
import org.restlet.data.Parameter;
import org.restlet.engine.header.ContentType;
import org.restlet.engine.header.DimensionWriter;
import org.restlet.engine.header.HeaderConstants;
import org.restlet.engine.header.HeaderUtils;
import org.restlet.engine.util.DateUtils;
import org.restlet.ext.jaxrs.internal.core.UnmodifiableMultivaluedMap;
import org.restlet.ext.jaxrs.internal.exceptions.IllegalPathException;
import org.restlet.ext.jaxrs.internal.exceptions.IllegalPathOnClassException;
import org.restlet.ext.jaxrs.internal.exceptions.IllegalPathOnMethodException;
import org.restlet.ext.jaxrs.internal.exceptions.ImplementationException;
import org.restlet.ext.jaxrs.internal.exceptions.InjectException;
import org.restlet.ext.jaxrs.internal.exceptions.JaxRsRuntimeException;
import org.restlet.ext.jaxrs.internal.exceptions.MethodInvokeException;
import org.restlet.ext.jaxrs.internal.exceptions.MissingAnnotationException;
import org.restlet.representation.EmptyRepresentation;
import org.restlet.representation.Representation;
import org.restlet.util.Series;
/**
* This class contains utility methods.
*
* @author Stephan Koops
*/
public class Util {
/**
* The default character set to be used, if no character set is given.
*/
public static final CharacterSet JAX_RS_DEFAULT_CHARACTER_SET = UTF_8;
/**
* The default character set to be used, if no character set is given, as
* String
*
* @see #JAX_RS_DEFAULT_CHARACTER_SET
*/
public static final String JAX_RS_DEFAULT_CHARACTER_SET_AS_STRING = JAX_RS_DEFAULT_CHARACTER_SET
.toString();
/**
* This comparator sorts the concrete MediaTypes to the beginning and the
* unconcrete to the end. The last is '*<!---->/*'
*/
public static final Comparator<org.restlet.data.MediaType> MEDIA_TYPE_COMP = new Comparator<org.restlet.data.MediaType>() {
public int compare(org.restlet.data.MediaType mediaType1,
org.restlet.data.MediaType mediaType2) {
if (mediaType1 == null) {
return mediaType2 == null ? 0 : 1;
}
if (mediaType2 == null) {
return -1;
}
if (mediaType1.equals(mediaType2, false)) {
return 0;
}
final int specNess1 = specificness(mediaType1);
final int specNess2 = specificness(mediaType2);
final int rt = specNess1 - specNess2;
if (rt != 0) {
return rt;
}
return mediaType1.toString().compareToIgnoreCase(
mediaType2.toString());
}
};
private static final byte NAME_READ = 2;
private static final byte NAME_READ_READY = 3;
private static final byte NAME_READ_START = 1;
/**
* The name of the header {@link MultivaluedMap}<String, String> in
* the attribute map.
*
*/
public static final String ORG_RESTLET_EXT_JAXRS_HTTP_HEADERS = "org.restlet.ext.jaxrs.http.headers";
/**
* The name of the header {@link Form} in the attribute map.
*
* @see #getHttpHeaders(Request)
* @see #getHttpHeaders(Response)
*/
public static final String ORG_RESTLET_HTTP_HEADERS = "org.restlet.http.headers";
/**
* appends the given String to the StringBuilder. If convertBraces is true,
* all "{" and "}" are converted to "%7B" and "%7D"
*
* @param stb
* the Appendable to append on
* @param string
* the CharSequence to append
* @param convertBraces
* if true, all braces are converted, if false then not.
* @throws IOException
* If the Appendable have a problem
*/
public static void append(Appendable stb, CharSequence string,
boolean convertBraces) throws IOException {
append(stb, string, convertBraces, 0, string.length());
}
/**
* appends the given String to the StringBuilder. If convertBraces is true,
* all "{" and "}" are converted to "%7B" and "%7D"
*
* @param stb
* the Appendable to append on
* @param string
* the CharSequence to append
* @param convertBraces
* if true, all braces are converted, if false then not.
* @param startIndex
* @throws IOException
* If the Appendable have a problem
*/
public static void append(Appendable stb, CharSequence string,
boolean convertBraces, int startIndex) throws IOException {
append(stb, string, convertBraces, startIndex, string.length());
}
/**
* appends the given String to the StringBuilder. If convertBraces is true,
* all "{" and "}" are converted to "%7B" and "%7D"
*
* @param stb
* the Appendable to append on
* @param string
* the CharSequence to append
* @param convertBraces
* if true, all braces are converted, if false then not.
* @param startIndex
* @param endIndex
* @throws IOException
* If the Appendable have a problem
*/
public static void append(Appendable stb, CharSequence string,
boolean convertBraces, int startIndex, int endIndex)
throws IOException {
if (!convertBraces) {
stb.append(string, startIndex, endIndex);
return;
}
for (int i = startIndex; i < endIndex; i++) {
final char c = string.charAt(i);
if (c == '{') {
stb.append("%7B");
} else if (c == '}') {
stb.append("%7D");
} else {
stb.append(c);
}
}
}
/**
* appends the array elements to the {@link StringBuilder}, separated by
* ", ".
*
* @param stb
* The {@link StringBuilder} to append the array elements.
* @param array
* The array to append to the {@link StringBuilder}.
*/
public static void append(StringBuilder stb, Object[] array) {
if ((array == null) || (array.length == 0)) {
return;
}
stb.append(array[0]);
for (int i = 1; i < array.length; i++) {
stb.append(", ");
stb.append(array[i]);
}
}
/**
* Checks, if the class is concrete.
*
* @param jaxRsClass
* JAX-RS root resource class or JAX-RS provider.
* @param typeName
* for the exception message "root resource class" or "provider"
* @throws IllegalArgumentException
* if the class is not concrete.
*/
public static void checkClassConcrete(Class<?> jaxRsClass, String typeName)
throws IllegalArgumentException {
final int modifiers = jaxRsClass.getModifiers();
if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers)) {
throw new IllegalArgumentException("The " + typeName + " "
+ jaxRsClass.getName() + " is not concrete");
}
}
/**
* Copies headers into a response.
*
* @param jaxRsHeaders
* Headers of an JAX-RS-Response.
* @param restletResponse
* Restlet Response to copy the headers in.
* @see javax.ws.rs.core.Response#getMetadata()
*/
public static void copyResponseHeaders(
final MultivaluedMap<String, Object> jaxRsHeaders,
Response restletResponse) {
Series<Parameter> headers = new Form();
for (Map.Entry<String, List<Object>> m : jaxRsHeaders.entrySet()) {
String headerName = m.getKey();
for (Object headerValue : m.getValue()) {
String hValue;
if (headerValue == null) {
hValue = null;
} else if (headerValue instanceof Date) {
hValue = formatDate((Date) headerValue, false);
} else {
hValue = headerValue.toString();
}
headers.add(new Parameter(headerName, hValue));
}
}
if (restletResponse.getEntity() == null) {
restletResponse.setEntity(new EmptyRepresentation());
}
HeaderUtils.copyResponseTransportHeaders(headers, restletResponse);
HeaderUtils.extractEntityHeaders(headers, restletResponse.getEntity());
// Copy extension headers
@SuppressWarnings("unchecked")
Series<Parameter> extensionHeaders = (Series<Parameter>) jaxRsHeaders
.getFirst(HeaderConstants.ATTRIBUTE_HEADERS);
if (extensionHeaders != null) {
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put(HeaderConstants.ATTRIBUTE_HEADERS, extensionHeaders);
restletResponse.setAttributes(attributes);
}
}
/**
* Copies the headers of the given {@link Response} into the given
* {@link Series}.
*
* @param restletResponse
* The response to update. Should contain a
* {@link Representation} to copy the representation headers from
* it.
* @return The copied headers.
*/
public static Series<Parameter> copyResponseHeaders(Response restletResponse) {
final Series<Parameter> headers = new Form();
HeaderUtils.addResponseHeaders(restletResponse, headers);
HeaderUtils.addEntityHeaders(restletResponse.getEntity(), headers);
return headers;
}
/**
* Creates an modifiable Collection with the given Objects in it, and no
* other objects. nulls will be ignored.
*
* @param objects
* @param <A>
* @return Returns the created list with the given objects in it.
*/
public static <A> Collection<A> createColl(A... objects) {
return createList(objects);
}
/**
* Creates an modifiable List with the given Object in it, and no other
* objects. If the given object is null, than an empty List will returned
*
* @param objects
* @param <A>
* @return Returns the created list with the given object in it or an empty
* list, if the given object is null.
*/
public static <A> List<A> createList(A... objects) {
final List<A> list = new ArrayList<A>();
final int l = objects.length;
for (int i = 0; i < l; i++) {
final A o = objects[i];
if (o != null) {
list.add(o);
}
}
return list;
}
/**
* Creates a map with the given keys and values.
*
* @param keysAndValues
* first element is key1, second element value1, third element
* key2, forth element value2 and so on.
* @return the created Map
*/
public static Map<String, String> createMap(String... keysAndValues) {
final Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < keysAndValues.length; i += 2) {
map.put(keysAndValues[i], keysAndValues[i + 1]);
}
return map;
}
/**
* Creates an modifiable Set with the given Object in it, and no other
* objects. If the given object is null, than an empty Set will returned.
*
* @param <A>
* @param objects
* @return the created Set
*/
public static <A> Set<A> createSet(A... objects) {
final Set<A> set = new HashSet<A>();
final int l = objects.length;
for (int i = 0; i < l; i++) {
final A o = objects[i];
if (o != null) {
set.add(o);
}
}
return set;
}
/**
* Checks, if the given class implements the given interface.
*
* @param clazz
* @param interfaze
* @return true, if the class implements the given interface, otherwise
* false. if the clazz or interfaze is null, false is returned
*/
public static boolean doesImplement(Class<?> clazz, Class<?> interfaze) {
if (clazz == null || interfaze == null)
return false;
if (doesImplement(clazz.getSuperclass(), interfaze))
return true;
for (Class<?> interf : clazz.getInterfaces()) {
if (interf.equals(interfaze))
return true;
if (doesImplement(interf, interfaze))
return true;
}
return false;
}
/**
* Checks, if the given CharSequence ends with the given character.
*
* @param charSequence
* @param character
* @return true, if the given charSequence ends with the given character,
* otherwise false.
* @see #notEndsWith(CharSequence, char)
* @see #startsWith(CharSequence, char)
*/
public static boolean endsWith(CharSequence charSequence, char character) {
if (charSequence == null)
return false;
if (charSequence.length() == 0)
return false;
return charSequence.charAt(charSequence.length() - 1) == character;
}
/**
* Check if the given objects are equal. Can deal with null references. if
* both elements are null, than the result is true.
*
* @param object1
* @param object2
* @return true, if the objects are equal.
*/
public static boolean equals(Object object1, Object object2) {
if (object1 == null) {
return object2 == null;
}
return object1.equals(object2);
}
/**
* Converts the given Date into a String. Copied from
* {@link org.restlet.engine.Call}.
*
* @param date
* Date to format
* @param cookie
* if true, using RFC 1036 format, otherwise RFC 1123 format.
* @return The formated date
*/
public static String formatDate(Date date, boolean cookie) {
if (cookie) {
return DateUtils.format(date, DateUtils.FORMAT_RFC_1036.get(0));
}
return DateUtils.format(date, DateUtils.FORMAT_RFC_1123.get(0));
}
/**
* Formats the given {@link Set} of {@link Dimension}s to a String for the
* HTTP Vary header.
*
* @param dimensions
* the dimensions to format.
* @return the Vary header or null, if dimensions is null or empty.
*/
public static String formatDimensions(Set<Dimension> dimensions) {
return DimensionWriter.write(dimensions);
}
/**
* @param genCompType
* @param forMessage
* @throws NegativeArraySizeException
* @throws ImplementationException
*/
private static Class<?> getArrayClass(Type genCompType, Type forMessage)
throws NegativeArraySizeException, ImplementationException {
if (genCompType.equals(Byte.TYPE)) {
return (new byte[0]).getClass();
}
if (genCompType.equals(Short.TYPE)) {
return (new short[0]).getClass();
}
if (genCompType.equals(Integer.TYPE)) {
return (new int[0]).getClass();
}
if (genCompType.equals(Long.TYPE)) {
return (new long[0]).getClass();
}
if (genCompType.equals(Float.TYPE)) {
return (new float[0]).getClass();
}
if (genCompType.equals(Double.TYPE)) {
return (new double[0]).getClass();
}
if (genCompType.equals(Character.TYPE)) {
return (new char[0]).getClass();
}
if (genCompType.equals(Boolean.TYPE)) {
return (new boolean[0]).getClass();
}
if (genCompType instanceof Class<?>) {
return Array.newInstance((Class<?>) genCompType, 0).getClass();
}
throw new ImplementationException("Sorry, could not handle a "
+ forMessage.getClass());
// LATER could not handle all classes
}
/**
* Returns the character set as String of the given http headers (e.g. from
* a JAX-RS {@link javax.ws.rs.core.Response}).
*
* @param httpHeaders
* the JAX-RS {@link javax.ws.rs.core.Response}
* @param defaultCs
* the character set to return, if no one could be found.
* @return the Restlet {@link CharacterSet} of the given http headers, or
* the given defaultCs as String if the header "Content-Type" is not
* available or has no parameter "charset". Returns only null, if
* the given defaultCs is null.
* @see #getSupportedCharSet(MultivaluedMap)
*/
public static String getCharsetName(
MultivaluedMap<String, Object> httpHeaders, CharacterSet defaultCs) {
String result = null;
CharacterSet charSet = null;
final Object contentType = httpHeaders.getFirst(CONTENT_TYPE);
if (contentType == null) {
charSet = defaultCs;
} else {
charSet = ContentType.readCharacterSet(contentType.toString());
if (charSet == null) {
charSet = defaultCs;
}
}
if (charSet != null) {
result = charSet.getName();
}
return result;
}
/**
* Returns the first element of the given collection. Throws an exception if
* the collection is empty.
*
* @param coll
* @param <A>
* @return Returns the first Element of the collection
* @throws NoSuchElementException
* If the collection is empty.
*/
public static <A> A getFirstElement(Collection<A> coll)
throws NoSuchElementException {
if (coll.isEmpty()) {
throw new NoSuchElementException(
"The Collection is empty; you can't get the first element of it.");
}
if (coll instanceof LinkedList<?>) {
return ((LinkedList<A>) coll).getFirst();
}
if (coll instanceof List<?>) {
return ((List<A>) coll).get(0);
}
return coll.iterator().next();
}
/**
* Returns the first element of the given {@link Iterable}. Throws an
* exception if the {@link Iterable} is empty.
*
* @param coll
* @param <A>
* @return Returns the first Element of the collection
* @throws NoSuchElementException
* If the collection is empty
*/
public static <A> A getFirstElement(Iterable<A> coll)
throws NoSuchElementException {
if (coll instanceof LinkedList<?>) {
return ((LinkedList<A>) coll).getFirst();
}
if (coll instanceof List<?>) {
return ((List<A>) coll).get(0);
}
return coll.iterator().next();
}
/**
* Returns the first element of the {@link List}. Throws an exception if the
* list is empty.
*
* @param list
* @param <A>
* @return Returns the first Element of the collection
* @throws IndexOutOfBoundsException
* If the list is empty
*/
public static <A> A getFirstElement(List<A> list)
throws IndexOutOfBoundsException {
if (list.isEmpty()) {
throw new IndexOutOfBoundsException(
"The Collection is empty; you can't get the first element of it.");
}
if (list instanceof LinkedList<?>) {
return ((LinkedList<A>) list).getFirst();
}
return list.get(0);
}
/**
* Returns the first element of the given {@link Iterable}. Returns null, if
* the {@link Iterable} is empty.
*
* @param coll
* @param <A>
* @return the first element of the collection, or null if the iterable is
* empty.
*/
public static <A> A getFirstElementOrNull(Iterable<A> coll) {
if (coll instanceof LinkedList<?>) {
final LinkedList<A> linkedList = ((LinkedList<A>) coll);
if (linkedList.isEmpty()) {
return null;
}
return linkedList.getFirst();
}
if (coll instanceof List<?>) {
final List<A> list = ((List<A>) coll);
if (list.isEmpty()) {
return null;
}
return list.get(0);
}
if (coll instanceof Collection<?>) {
if (((Collection<A>) coll).isEmpty()) {
return null;
}
}
return coll.iterator().next();
}
/**
* Returns the first entry of the given {@link Map}. Throws an exception if
* the Map is empty.
*
* @param map
* @param <K>
* @param <V>
* @return the first entry of the given {@link Map}.
* @throws NoSuchElementException
* If the map is empty.
*/
public static <K, V> Map.Entry<K, V> getFirstEntry(Map<K, V> map)
throws NoSuchElementException {
return map.entrySet().iterator().next();
}
/**
* Returns the key of the first entry of the given {@link Map}. Throws an
* exception if the Map is empty.
*
* @param map
* @param <K>
* @param <V>
* @return the key of the first entry of the given {@link Map}.
* @throws NoSuchElementException
* If the map is empty.
*/
public static <K, V> K getFirstKey(Map<K, V> map)
throws NoSuchElementException {
return map.keySet().iterator().next();
}
/**
* Returns the value of the first entry of the given {@link Map}. Throws an
* exception if the Map is empty.
*
* @param map
* @param <K>
* @param <V>
* @return the value of the first entry of the given {@link Map}.
* @throws NoSuchElementException
* If the map is empty.
*/
public static <K, V> V getFirstValue(Map<K, V> map)
throws NoSuchElementException {
return map.values().iterator().next();
}
/**
*
* @param clazz
* The class which implemented interface should be checked.
* @param implInterface
* the interface from which the generic type should be returned.
* @return the type parameter of the given class.
*/
public static Class<?> getGenericClass(Class<?> clazz,
Class<?> implInterface) {
if (clazz == null)
throw new IllegalArgumentException("The class must not be null");
if (implInterface == null)
throw new IllegalArgumentException(
"The interface to b eimplemented must not be null");
return getGenericClass(clazz, implInterface, null);
}
private static Class<?> getGenericClass(Class<?> clazz,
Class<?> implInterface, Type[] gsatp) {
for (Type ifGenericType : clazz.getGenericInterfaces()) {
if (!(ifGenericType instanceof ParameterizedType)) {
continue;
}
final ParameterizedType pt = (ParameterizedType) ifGenericType;
Type ptRawType = pt.getRawType();
if (ptRawType == null)
continue;
if (!ptRawType.equals(implInterface))
continue;
final Type[] atps = pt.getActualTypeArguments();
if (atps == null || atps.length == 0)
continue;
final Type atp = atps[0];
if (atp instanceof Class<?>) {
return (Class<?>) atp;
}
if (atp instanceof ParameterizedType) {
final Type rawType = ((ParameterizedType) atp).getRawType();
if (rawType instanceof Class<?>) {
return (Class<?>) rawType;
}
}
if (atp instanceof TypeVariable<?>) {
TypeVariable<?> tv = (TypeVariable<?>) atp;
String name = tv.getName();
if (name == null)
continue;
// clazz = AbstractProvider
// implInterface = MessageBodyReader
// name = "T"
// pt = MessageBodyReader<T>
for (int i = 0; i < atps.length; i++) {
TypeVariable<?> tv2 = (TypeVariable<?>) atps[i];
String tv2Name = tv2.getName();
if (tv2Name == null)
continue;
if (tv2Name.equals(name)) {
Type gsatpn = gsatp[i];
if (gsatpn instanceof Class<?>) {
return (Class<?>) gsatpn;
}
if (gsatpn instanceof ParameterizedType) {
final Type rawType = ((ParameterizedType) gsatpn)
.getRawType();
if (rawType instanceof Class<?>)
return (Class<?>) rawType;
throw new ImplementationException(
"Sorry, don't know how to return the class here");
}
if (gsatpn instanceof GenericArrayType) {
Type genCompType = ((GenericArrayType) gsatpn)
.getGenericComponentType();
return getArrayClass(genCompType, gsatpn);
}
// if(gsatpn instanceof TypeVariable) {
// TypeVariable<Class<?>> tvn = (TypeVariable)gsatpn;
// Class<?> cl = tvn.getGenericDeclaration();
// Type[] boulds = tvn.getBounds();
// cl.toString();
// }
// throw new
// ImplementationException("Sorry, could not handle a "
// +gsatpn.getClass());
}
}
}
}
Class<?> superClass = clazz.getSuperclass();
Type genericSuperClass = clazz.getGenericSuperclass();
if ((genericSuperClass instanceof Class<?>)) {
if (!implInterface.isAssignableFrom((Class<?>) genericSuperClass)) {
// if the superclass doesn't implemented the
// required interface, give up here ...
return null;
}
}
// ... otherwise, obtain type arguments from
// superclass if it's a parameterized type ...
if ((gsatp == null) && (genericSuperClass instanceof ParameterizedType)) {
// LATER this is a hack
gsatp = ((ParameterizedType) genericSuperClass)
.getActualTypeArguments();
}
// ... or make a recursion for a non-parameterized superclass.
if (superClass != null)
return getGenericClass(superClass, implInterface, gsatp);
return null;
}
/**
* Example: in List<String> -> out: String.class
*
* @param genericType
* @return otherwise null
*/
public static Class<?> getGenericClass(Type genericType) {
if (!(genericType instanceof ParameterizedType)) {
return null;
}
final ParameterizedType pt = (ParameterizedType) genericType;
Type[] actualTypeArguments = pt.getActualTypeArguments();
if (actualTypeArguments == null || actualTypeArguments.length == 0)
return null;
final Type atp = actualTypeArguments[0];
if (atp instanceof Class<?>) {
return (Class<?>) atp;
}
if (atp instanceof ParameterizedType) {
final Type rawType = ((ParameterizedType) atp).getRawType();
if (rawType instanceof Class<?>) {
return (Class<?>) rawType;
}
return null;
}
return null;
}
/**
* Returns the HTTP headers of the Restlet {@link Request} as {@link Form}.
*
* @param request
* @return Returns the HTTP headers of the Request.
*/
public static Form getHttpHeaders(Request request) {
Form headers = (Form) request.getAttributes().get(
ORG_RESTLET_HTTP_HEADERS);
if (headers == null) {
headers = new Form();
request.getAttributes().put(ORG_RESTLET_HTTP_HEADERS, headers);
}
return headers;
}
/**
* Returns the HTTP headers of the Restlet {@link Response} as {@link Form}.
*
* @param response
* @return Returns the HTTP headers of the Response.
*/
public static Form getHttpHeaders(Response response) {
Form headers = (Form) response.getAttributes().get(
ORG_RESTLET_HTTP_HEADERS);
if (headers == null) {
headers = new Form();
response.getAttributes().put(ORG_RESTLET_HTTP_HEADERS, headers);
}
return headers;
}
/**
* Returns the request headers as {@link MultivaluedMap}.
*
* @param request
* @return the request headers as {@link MultivaluedMap}.
*/
public static MultivaluedMap<String, String> getJaxRsHttpHeaders(
Request request) {
final Map<String, Object> attrsOfRequ = request.getAttributes();
@SuppressWarnings("unchecked")
MultivaluedMap<String, String> headers = (MultivaluedMap<String, String>) attrsOfRequ
.get(ORG_RESTLET_EXT_JAXRS_HTTP_HEADERS);
if (headers == null) {
headers = UnmodifiableMultivaluedMap.getFromForm(
getHttpHeaders(request), false);
attrsOfRequ.put(ORG_RESTLET_EXT_JAXRS_HTTP_HEADERS, headers);
}
return headers;
}
/**
* Returns the last element of the given {@link Iterable}. Throws an
* exception if the given iterable is empty.
*
* @param iterable
* @param <A>
* @return Returns the last element of the {@link Iterable}
* @throws IndexOutOfBoundsException
* If the {@link Iterable} is a {@link List} and its is empty.
* @throws NoSuchElementException
* If the {@link Iterable} is empty and the {@link Iterable} is
* not a {@link List}.
*/
public static <A> A getLastElement(Iterable<A> iterable)
throws IndexOutOfBoundsException, NoSuchElementException {
if (iterable instanceof LinkedList<?>) {
return ((LinkedList<A>) iterable).getLast();
}
if (iterable instanceof List<?>) {
final List<A> list = ((List<A>) iterable);
return list.get(list.size() - 1);
}
return getLastElement(iterable.iterator());
}
/**
* Returns the last element of the given {@link Iterator}. Throws an
* exception if the given iterator is empty.
*
* @param iter
* @param <A>
* @return Returns the last element of the {@link Iterator}.
* @throws NoSuchElementException
* If the {@link Iterator} is empty.
*/
public static <A> A getLastElement(Iterator<A> iter)
throws NoSuchElementException {
A e = iter.next();
while (iter.hasNext()) {
e = iter.next();
}
return e;
}
/**
* Returns the last element of the given {@link List}. Throws an exception
* if the given list is empty.
*
* @param list
* @param <A>
* @return Returns the last element of the list
* @throws IndexOutOfBoundsException
* If the list is empty
*/
public static <A> A getLastElement(List<A> list)
throws IndexOutOfBoundsException {
if (list instanceof LinkedList<?>) {
return ((LinkedList<A>) list).getLast();
}
return list.get(list.size() - 1);
}
/**
* Returns the last element of the given {@link Iterable}, or null, if the
* iterable is empty. Returns null, if the iterable is empty.
*
* @param iterable
* @param <A>
* @return Returns the last Element of the {@link Iterable}, or null if it
* is empty.
*/
public static <A> A getLastElementOrNull(Iterable<A> iterable) {
if (iterable instanceof LinkedList<?>) {
final LinkedList<A> linkedList = ((LinkedList<A>) iterable);
if (linkedList.isEmpty()) {
return null;
}
return linkedList.getLast();
}
if (iterable instanceof List<?>) {
final List<A> list = ((List<A>) iterable);
if (list.isEmpty()) {
return null;
}
return list.get(list.size() - 1);
}
if (iterable instanceof Collection<?>) {
if (((Collection<A>) iterable).isEmpty()) {
return null;
}
}
return getLastElementOrNull(iterable.iterator());
}
/**
* Returns the last element of the given {@link Iterator}, or null, if the
* iterator is empty. Returns null, if the iterator is empty.
*
* @param iter
* @param <A>
* @return Returns the last Element of the {@link Iterator}.
*/
public static <A> A getLastElementOrNull(Iterator<A> iter) {
A e = null;
while (iter.hasNext()) {
e = iter.next();
}
return e;
}
/**
* Returns the Restlet {@link MediaType} of the given http headers (e.g.
* from a JAX-RS {@link javax.ws.rs.core.Response}).
*
* @param httpHeaders
* the JAX-RS {@link javax.ws.rs.core.Response}
* @return the Restlet {@link MediaType} of the given http headers, or null,
* if the header "Content-Type" is not available.
*/
public static MediaType getMediaType(
MultivaluedMap<String, Object> httpHeaders) {
final Object contentType = httpHeaders.getFirst(CONTENT_TYPE);
if (contentType == null) {
return null;
}
if (contentType instanceof MediaType) {
return (MediaType) contentType;
}
if (contentType instanceof javax.ws.rs.core.MediaType) {
return Converter
.toRestletMediaType((javax.ws.rs.core.MediaType) contentType);
}
return ContentType.readMediaType(contentType.toString());
}
/**
* Returns all public {@link Method}s of the class with the given name
* (case-sensitive)
*
* @param clazz
* The {@link Class} to search the {@link Method}s.
* @param methodName
* The name of the {@link Method} to search.
* @return Returns a {@link Collection} all of {@link Method}s with the
* given name. Never returns null. If no methods are found an empty
* Collection will be returned. The method {@link Iterator#remove()}
* of this collection is supported.
* @throws IllegalArgumentException
* if the clazz or the method name is null.
*/
public static Collection<Method> getMethodsByName(Class<?> clazz,
String methodName) throws IllegalArgumentException {
if (clazz == null) {
throw new IllegalArgumentException("The class must not be null");
}
if (methodName == null) {
throw new IllegalArgumentException(
"The method name must not be null");
}
final Collection<Method> methods = new ArrayList<Method>(2);
for (final Method method : clazz.getMethods()) {
if (method.getName().equals(methodName)) {
methods.add(method);
}
}
return methods;
}
/**
* Returns the only element of the list, or null, if the List is null or
* empty.
*
* @param <A>
* @param list
* a List with at most one element
* @return The element of the List, or null, if there is no element.
* @throws IllegalArgumentException
* if the list contains more than one element.
*/
public static <A> A getOnlyElement(Collection<A> list)
throws IllegalArgumentException {
if (list == null) {
return null;
}
if (list.isEmpty()) {
return null;
}
if (list.size() > 1) {
throw new IllegalArgumentException(
"The list must have exactly one element");
}
if (list instanceof List<?>) {
return ((List<A>) list).get(0);
}
return list.iterator().next();
}
/**
* Returns the Name of the only element of the list of the given Metadata.
* Returns null, if the list is empty or null.
*
* @param metadatas
* @return the name of the Metadata
* @see #getOnlyElement(List)
*/
public static String getOnlyMetadataName(List<? extends Metadata> metadatas) {
final Metadata metadata = getOnlyElement(metadatas);
if (metadata == null) {
return null;
}
return metadata.getName();
}
/**
* Returns the @{@link Path} annotation of the given root resource
* class.
*
* @param jaxRsClass
* the root resource class.
* @return the @{@link Path} annotation of the given root resource
* class.
* @throws MissingAnnotationException
* if the path annotation is missing
* @throws IllegalArgumentException
* if the jaxRsClass is null.
*/
public static Path getPathAnnotation(Class<?> jaxRsClass)
throws MissingAnnotationException, IllegalArgumentException {
if (jaxRsClass == null) {
throw new IllegalArgumentException(
"The jaxRsClass must not be null");
}
final Path path = jaxRsClass.getAnnotation(Path.class);
if (path == null) {
throw new MissingAnnotationException(
"The root resource class does not have a @Path annotation");
}
return path;
}
/**
* Returns the @{@link Path} annotation of the given sub resource
* locator. Throws an exception if no @{@link Path} annotation is
* available.
*
* @param method
* the java method to get the @Path from
* @return the @Path annotation.
* @throws IllegalArgumentException
* if null was given.
* @throws MissingAnnotationException
* if the annotation is not present.
*/
public static Path getPathAnnotation(Method method)
throws IllegalArgumentException, MissingAnnotationException {
if (method == null) {
throw new IllegalArgumentException(
"The root resource class must not be null");
}
final Path path = method.getAnnotation(Path.class);
if (path == null) {
throw new MissingAnnotationException("The method "
+ method.getName() + " does not have an annotation @Path");
}
return path;
}
/**
* Returns the @{@link Path} annotation of the given sub resource
* locator. Returns null if no @{@link Path} annotation is available.
*
* @param method
* the java method to get the @Path from
* @return the @Path annotation or null, if not present.
* @throws IllegalArgumentException
* if the method is null.
*/
public static Path getPathAnnotationOrNull(Method method)
throws IllegalArgumentException {
if (method == null) {
throw new IllegalArgumentException(
"The root resource class must not be null");
}
return method.getAnnotation(Path.class);
}
/**
* Returns the perhaps decoded template of the path annotation.
*
* @param resource
* @return Returns the path template as String. Never returns null.
* @throws IllegalPathOnClassException
* @throws MissingAnnotationException
* @throws IllegalArgumentException
*/
public static String getPathTemplateWithoutRegExps(Class<?> resource)
throws IllegalPathOnClassException, MissingAnnotationException,
IllegalArgumentException {
try {
return getPathTemplateWithoutRegExps(Util
.getPathAnnotation(resource));
} catch (IllegalPathException e) {
throw new IllegalPathOnClassException(e);
}
}
/**
* Returns the path template of the given sub resource locator or sub
* resource method. It is encoded (if necessary) and valid.
*
* @param method
* the java method
* @return the path template
* @throws IllegalPathOnMethodException
* @throws IllegalArgumentException
* @throws MissingAnnotationException
*/
public static String getPathTemplateWithoutRegExps(Method method)
throws IllegalArgumentException, IllegalPathOnMethodException,
MissingAnnotationException {
final Path path = getPathAnnotation(method);
try {
return getPathTemplateWithoutRegExps(path);
} catch (IllegalPathException e) {
throw new IllegalPathOnMethodException(e);
}
}
/**
* Returns the path from the annotation. It will be encoded if necessary. If
* it should not be encoded, this method checks, if all characters are
* valid.
*
* @param path
* The {@link Path} annotation. Must not be null.
* @return the encoded path template
* @throws IllegalPathException
* @see Path#encode()
*/
public static String getPathTemplateWithoutRegExps(final Path path)
throws IllegalPathException {
return getPathTemplateWithoutRegExps(path.value(), path);
}
/**
* @param pathTemplate
* @param pathForExcMess
* @return
* @throws IllegalPathException
*/
public static String getPathTemplateWithoutRegExps(
final String pathTemplate, final Path pathForExcMess)
throws IllegalPathException {
final StringBuilder stb = new StringBuilder();
final int l = pathTemplate.length();
for (int i = 0; i < l; i++) {
final char c = pathTemplate.charAt(i);
if (c == '{') {
i = processTemplVarname(pathTemplate, i, stb, pathForExcMess);
} else if (c == '%') {
try {
EncodeOrCheck.processPercent(i, true, pathTemplate, stb);
} catch (IllegalArgumentException e) {
throw new IllegalPathException(pathForExcMess, e);
}
} else if (c == '}') {
throw new IllegalPathException(pathForExcMess,
"'}' is only allowed as "
+ "end of a variable name in \"" + pathTemplate
+ "\"");
} else if (c == ';') {
throw new IllegalPathException(pathForExcMess,
"A semicolon is not allowed in a path");
} else if (c == '/') {
stb.append(c);
} else {
EncodeOrCheck.encode(c, stb);
}
}
return stb.toString();
}
/**
* Returns the given character set, if it is supported on this system.
* Returns UTF-8 otherwise.
*
* @param characterSet
* the wished {@link CharacterSet}
* @return a supported {@link CharacterSet}, never null.
* @see #getCharsetName(MultivaluedMap, CharacterSet)
*/
public static CharacterSet getSupportedCharSet(CharacterSet characterSet) {
if (characterSet == null) {
return JAX_RS_DEFAULT_CHARACTER_SET;
}
if (Charset.isSupported(characterSet.toString())) {
return characterSet;
}
return logUnsupportedCharSet(characterSet.toString());
}
/**
* Returns the character set of the http response headers. If no character
* set is available or it is not supported, UFT-8 is returned.
*
* @param httpResponseHeaders
* @return a supported {@link CharacterSet}, never null.
* @see #getCharsetName(MultivaluedMap, CharacterSet)
*/
public static CharacterSet getSupportedCharSet(
MultivaluedMap<String, Object> httpResponseHeaders) {
final String csn = getCharsetName(httpResponseHeaders,
JAX_RS_DEFAULT_CHARACTER_SET);
if (Charset.isSupported(csn)) {
return CharacterSet.valueOf(csn);
}
return logUnsupportedCharSet(csn);
}
/**
* Returns the index of the first ";" in the last path segment.
*
* @param path
* the path, without a query.
* @return the index of the first ";" in the last path segment. Returns -1,
* if no ';' is available.
*/
public static int indexBeginMatrixOfLastSegment(CharSequence path) {
// NICE optimize for speed; avoid conversion to String, if it is faster.
String pathStr = path.toString();
return pathStr.indexOf(';', pathStr.lastIndexOf('/'));
}
/**
* Returns the index of the first occurrence of the given character between
* the given indexes.
*
* @param charSequence
* the char sequence to look in
* @param c
* the character to look for.
* @param beginIndex
* @param endIndex
* @return the index of the given character between the given indexes, or -1
* if the character could not be found in the given range.
* @see String#indexOf(String, int)
* @see String#substring(int, int)
*/
public static int indexOfBetween(CharSequence charSequence, char c,
int beginIndex, int endIndex) {
if (beginIndex < 0) {
beginIndex = 0;
}
if (endIndex < 0) {
endIndex = 0;
}
for (int i = beginIndex; i < endIndex; i++) {
char csc = charSequence.charAt(i);
if (csc == c) {
return i;
}
}
return -1;
}
/**
* Injects the given toInject in the resource field or the given bean
* setter.
*
* @param resource
* @param fieldOrBeanSetter
* @param toInject
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws InjectException
*/
public static void inject(Object resource,
AccessibleObject fieldOrBeanSetter, Object toInject)
throws InvocationTargetException, IllegalArgumentException,
InjectException {
if (fieldOrBeanSetter instanceof Field) {
inject(resource, (Field) fieldOrBeanSetter, toInject);
} else if (fieldOrBeanSetter instanceof Method) {
inject(resource, (Method) fieldOrBeanSetter, toInject);
} else {
throw new IllegalArgumentException(
"The fieldOrBeanSetter must be a java.lang.reflect.Field or a java.lang.reflect.Method");
}
}
/**
* Inject the given toInject into the given field in the given resource (or
* whatever)
*
* @param resource
* the concrete Object to inject the other object in. If the
* field is static, thsi object may be null.
* @param field
* the field to inject the third parameter in.
* @param toInject
* the object to inject in the first parameter object.
* @throws InjectException
* if the injection was not possible. See
* {@link InjectException#getCause()} for the reason.
*/
public static void inject(final Object resource, final Field field,
final Object toInject) throws InjectException {
try {
final IllegalAccessException iae = AccessController
.doPrivileged(new PrivilegedAction<IllegalAccessException>() {
public IllegalAccessException run() {
try {
field.set(resource, toInject);
return null;
} catch (IllegalAccessException e) {
return e;
}
}
});
if (iae != null) {
throw new InjectException("Could not inject the "
+ toInject.getClass() + " into field " + field
+ " of object " + resource, iae);
}
} catch (RuntimeException e) {
throw new InjectException("Could not inject the "
+ toInject.getClass() + " into field " + field
+ " of object " + resource, e);
}
}
/**
* Injects the given toInject in the resource with the given bean setter.
*
* @param resource
* @param beanSetter
* @param toInject
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws InjectException
*/
public static void inject(Object resource, Method beanSetter,
Object toInject) throws InvocationTargetException,
IllegalArgumentException, InjectException {
try {
invokeMethod(resource, beanSetter, toInject);
} catch (MethodInvokeException mie) {
throw new InjectException(mie);
}
}
/**
* Invokes the given method without parameters. This constraint is not
* checked; but the method could also be called, if access is normally not
* allowed.<br>
* If no javaMethod is given, nothing happens.
*
* @param object
* the object to call the method on.
* @param javaMethod
* the method to call
* @param args
* the arguments of the method
* @throws MethodInvokeException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* if at least one argument does not match the required method
* parameters.
* @see #inject(Object, Field, Object)
* @see #findPostConstructMethod(Class)
* @see #findPreDestroyMethod(Class)
*/
public static void invokeMethod(final Object object,
final Method javaMethod, final Object... args)
throws MethodInvokeException, InvocationTargetException,
IllegalArgumentException {
if (javaMethod == null) {
return;
}
try {
AccessController
.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
javaMethod.invoke(object, args);
return null;
}
});
} catch (PrivilegedActionException e) {
final Throwable cause = e.getCause();
if (cause instanceof IllegalAccessException) {
throw new MethodInvokeException(
"Not allowed to invoke post construct method "
+ javaMethod, cause);
}
if (cause instanceof InvocationTargetException) {
throw (InvocationTargetException) cause;
}
if (cause instanceof ExceptionInInitializerError) {
throw new MethodInvokeException(
"Could not invoke post construct method " + javaMethod,
cause);
}
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
throw new JaxRsRuntimeException(
"Error while invoking post construct method " + javaMethod,
cause);
}
}
/**
* Checks, if the list is empty or null.
*
* @param list
* @return true, if the list is empty or null, or false, if the list
* contains elements.
* @see #isEmpty(Object[])
*/
public static boolean isEmpty(List<?> list) {
return ((list == null) || list.isEmpty());
}
/**
* Tests, if the given array is empty or null. Will not throw a
* NullPointerException.
*
* @param array
* @return Returns true, if the given array ist null or has zero elements,
* otherwise false.
* @see #isEmpty(List)
*/
public static boolean isEmpty(Object[] array) {
if ((array == null) || (array.length == 0)) {
return true;
}
return false;
}
/**
* Tests, if the given String is null, empty or "/". Will not throw a
* NullPointerException.
*
* @param string
* @return Returns true, if the given string ist null, empty or equals "/",
* otherwise false.
*/
public static boolean isEmptyOrSlash(String string) {
return (string == null) || (string.length() == 0) || string.equals("/");
}
/**
* Checks, if the list contains elements.
*
* @param list
* @return true, if the list contains elements, or false, if the list is
* empty or null.
*/
public static boolean isNotEmpty(List<?> list) {
return ((list != null) && !list.isEmpty());
}
/**
* Checks, if the given class is a JAX-RS provider.
*
* @param jaxRsClass
* the class to check
* @return true, if the class is a JAX-RS provider, otherwise false.
*/
public static boolean isProvider(Class<?> jaxRsClass) {
return jaxRsClass.isAnnotationPresent(javax.ws.rs.ext.Provider.class);
}
/**
* Checks, if the given class is a JAX-RS root resource class.
*
* @param jaxRsClass
* the class to check
* @return true, if the class is a JAX-RS root resource class, otherwise
* false.
*/
public static boolean isRootResourceClass(Class<?> jaxRsClass) {
return jaxRsClass.isAnnotationPresent(javax.ws.rs.Path.class);
}
/**
* Logs a message that the wished character set is not supported and UTF-8
* is used.
*
* @param csn
* @return UFT-8
*/
private static CharacterSet logUnsupportedCharSet(String charsetName) {
Context.getCurrentLogger().warning(
"The character set " + charsetName + " is not "
+ "available. Will use "
+ JAX_RS_DEFAULT_CHARACTER_SET_AS_STRING);
return JAX_RS_DEFAULT_CHARACTER_SET;
}
/**
* Checks, if the given CharSequence ends with the given character.
*
* @param charSequence
* @param character
* @return true, if the given charSequence ends with the given character,
* otherwise false.
* @see #endsWith(CharSequence, char)
* @see #notStartsWith(CharSequence, char)
*/
public static boolean notEndsWith(CharSequence charSequence, char character) {
if (charSequence == null)
return true;
if (charSequence.length() == 0)
return true;
return charSequence.charAt(charSequence.length() - 1) != character;
}
/**
* Checks, if the given CharSequence starts with the given character.
*
* @param charSequence
* @param character
* @return true, if the given charSequence starts with the given character,
* otherwise false.
* @see #startsWith(CharSequence, char)
* @see #notEndsWith(CharSequence, char)
*/
public static boolean notStartsWith(CharSequence charSequence,
char character) {
if (charSequence == null)
return true;
if (charSequence.length() == 0)
return true;
return charSequence.charAt(0) != character;
}
/**
* @param pathTemplate
* @param braceIndex
* @param stb
* @param pathForExcMess
* @throws IllegalPathException
*/
private static int processTemplVarname(final String pathTemplate,
final int braceIndex, final StringBuilder stb,
final Path pathForExcMess) throws IllegalPathException {
final int l = pathTemplate.length();
stb.append('{');
int state = NAME_READ_START;
for (int i = braceIndex + 1; i < l; i++) {
final char c = pathTemplate.charAt(i);
if (c == '{') {
throw new IllegalPathException(pathForExcMess,
"A variable must not " + "contain an extra '{' in \""
+ pathTemplate + "\"");
} else if (c == ' ' || c == '\t') {
if (state == NAME_READ)
state = NAME_READ_READY;
continue;
} else if (c == ':') {
if (state == NAME_READ_START) {
throw new IllegalPathException(pathForExcMess,
"The variable name at position must not be null at "
+ braceIndex + " of \"" + pathTemplate
+ "\"");
}
if (state == NAME_READ || state == NAME_READ_READY) {
for (int j = i; j < l; j++) {
if (pathTemplate.charAt(j) == '}') {
stb.append('}');
return j;
}
}
throw new IllegalPathException(pathForExcMess,
"No '}' found after '{' at position " + braceIndex
+ " of \"" + pathTemplate + "\"");
}
} else if (c == '}') {
if (state == NAME_READ_START) {
throw new IllegalPathException(pathForExcMess,
"The template variable name '{}' is not allowed in "
+ "\"" + pathTemplate + "\"");
}
stb.append('}');
return i;
}
if (state == NAME_READ_START) {
state = NAME_READ;
stb.append(c);
} else if (state == NAME_READ) {
stb.append(c);
} else {
throw new IllegalPathException(pathForExcMess,
"Invalid character found at position " + i + " of \""
+ pathTemplate + "\"");
}
}
throw new IllegalPathException(pathForExcMess,
"No '}' found after '{' " + "at position " + braceIndex
+ " of \"" + pathTemplate + "\"");
}
/**
* Returns a new {@link List}, which contains all
* {@link org.restlet.data.MediaType}s of the given List, sorted by it's
* concreteness, the concrete {@link org.restlet.data.MediaType} at the
* beginning.
*
* @param mediaTypes
* @return the sorted media types
* @see Util#specificness(org.restlet.data.MediaType)
*/
public static List<org.restlet.data.MediaType> sortByConcreteness(
Collection<org.restlet.data.MediaType> mediaTypes) {
final List<org.restlet.data.MediaType> newList = new ArrayList<org.restlet.data.MediaType>(
mediaTypes.size());
for (final org.restlet.data.MediaType mediaType : mediaTypes) {
if (specificness(mediaType) > 0) {
newList.add(mediaType);
}
}
for (final org.restlet.data.MediaType mediaType : mediaTypes) {
if (specificness(mediaType) == 0) {
newList.add(mediaType);
}
}
for (final org.restlet.data.MediaType mediaType : mediaTypes) {
if (specificness(mediaType) < 0) {
newList.add(mediaType);
}
}
return newList;
}
/**
* Returns the specificness of the given {@link org.restlet.data.MediaType}:
* <ul>
* <li>1 for any concrete type (contains no star)</li>
* <li>0 for the types (anything/*)</li>
* <li>-1 for '*<!---->/*</li>
* </ul>
*
* @param mediaType
* @return 1, 0 or -1
* @see #isConcrete(org.restlet.data.MediaType)
* @see #sortByConcreteness(Collection)
*/
public static int specificness(org.restlet.data.MediaType mediaType) {
if (mediaType.equals(org.restlet.data.MediaType.ALL, true)) {
return -1;
}
if (mediaType.getSubType().equals("*")) {
return 0;
}
return 1;
}
/**
* Checks, if the given CharSequence starts with the given character.
*
* @param charSequence
* @param character
* @return true, if the given charSequence starts with the given character,
* otherwise false.
* @see #notStartsWith(CharSequence, char)
* @see #endsWith(CharSequence, char)
*/
public static boolean startsWith(CharSequence charSequence, char character) {
if (charSequence == null)
return false;
if (charSequence.length() == 0)
return false;
return charSequence.charAt(0) == character;
}
/**
* Creates an Array of the given type.
*
* @param coll
* @param arrayType
* @return the object array
* @throws NegativeArraySizeException
*/
public static Object[] toArray(Collection<?> coll, Class<?> arrayType) {
final int collSize = coll.size();
final Object[] array = (Object[]) Array
.newInstance(arrayType, collSize);
return coll.toArray(array);
}
/**
* Concatenate the members of the Set to a String, separated with the given
* delimiter.
*
* @param collection
* @param delimiter
* @return the concatenated
*/
public static String toString(Collection<?> collection, String delimiter) {
if ((collection == null) || collection.isEmpty()) {
return "";
}
final Iterator<?> iterator = collection.iterator();
if (collection.size() == 1) {
return String.valueOf(iterator.next());
}
final StringBuilder stb = new StringBuilder();
stb.append(iterator.next());
while (iterator.hasNext()) {
final Object object = iterator.next();
stb.append(delimiter);
stb.append(object);
}
return stb.toString();
}
}