/*******************************************************************************
* Copyright (c) 2006-2013, Cloudsmith Inc.
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the copyright holder
* listed above, as the Initial Contributor under such license. The text or
* such license is available at www.eclipse.org.
******************************************************************************/
package org.eclipse.buckminster.core.helpers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.buckminster.core.Messages;
import org.eclipse.osgi.util.NLS;
/**
* @author Filip Hrbek
*
* The <CODE>SmartArrayList</CODE> class extends class ArrayList. It has
* the same counstructors. It has methods list transformations (reverse,
* rotate, shuffle) and easy-to-use formatted printing its values (using
* the constructor with element mapper).<BR>
*/
public class SmartArrayList<E> extends ArrayList<E> {
/**
* Serial UID
*/
private static final long serialVersionUID = 5853596854483494180L;
/**
* Creates a SmartArrayList of String from comma separated values. If a
* value should contain spaces, commas or double quotes, it should be double
* quoted. Any backslashes must be doubled unless they are used as escape
* characters for double quotes inside a string.<BR>
*
* Examples:<BR>
* fromCommaSeparatedStrings("abc, def, ghi");<BR>
* fromCommaSeparatedStrings("\"ab c\", \"de\\\"f\", \"gh,i\"");<BR>
*
* @param values
* Initial comma separated values
* @return List of values
* @throws Exception
* if the string cannot be parsed
*/
public static SmartArrayList<String> fromCommaSeparatedStrings(String values) throws Exception {
return fromSeparatedStrings(values, ',');
}
/**
* Creates a SmartArrayList of String from delimiter separated values. If a
* value should contain spaces, delimiters itself or double quotes, it
* should be double quoted. Any backslashes must be doubled unless they are
* used as escape characters for double quotes inside a string.<BR>
*
* Examples:<BR>
* fromSeparatedStrings("abc, def, ghi", ',');<BR>
* fromSeparatedStrings("\"ab c\", \"de\\\"f\", \"gh,i\"", ',');<BR>
* fromSeparatedStrings("\"ab c\"def, abc\"de\\\"f\", abcdef\"gh,i\"jkl",
* ',');<BR>
*
* @param values
* Initial comma separated values
* @param delimiter
* Delimitting character
* @return List of values
* @throws Exception
* if the string cannot be parsed
*/
public static SmartArrayList<String> fromSeparatedStrings(String values, char delimiter) throws Exception {
final String bareStringPattern = "(?:[^\\\\\"" + delimiter + "]|\\\\.)+"; //$NON-NLS-1$ //$NON-NLS-2$
final String doubleQuotedStringPattern = "\"(?:[^\"\\\\]|\\\\.)*\""; //$NON-NLS-1$
final Pattern tokenPattern = Pattern.compile("^\\s*((?:" + bareStringPattern + "|" + doubleQuotedStringPattern //$NON-NLS-1$ //$NON-NLS-2$
+ ")*)(?:\\s*" + delimiter + "(.*))?$"); //$NON-NLS-1$ //$NON-NLS-2$
final Pattern elementPattern = Pattern.compile("(" + bareStringPattern + "|" + doubleQuotedStringPattern + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Matcher tokenMatcher = tokenPattern.matcher(values);
SmartArrayList<String> result = new SmartArrayList<String>();
boolean firstMatch = false;
while (tokenMatcher.matches()) {
firstMatch = true;
String value = tokenMatcher.group(1);
StringBuilder strippedValue = new StringBuilder();
Matcher elementMatcher = elementPattern.matcher(value);
while (elementMatcher.find()) {
String aux = elementMatcher.group(1);
if (aux.startsWith("\"")) //$NON-NLS-1$
{
if (!aux.endsWith("\"")) //$NON-NLS-1$
{
throw new Exception(NLS.bind(Messages.Unbalanced_double_quotes_0, values));
}
aux = aux.substring(1, aux.length() - 1);
}
strippedValue.append(aux);
}
result.add(StringUtil.stripBackslashes(strippedValue.toString()));
String rest = tokenMatcher.group(2);
if (rest == null) {
break;
}
tokenMatcher = tokenPattern.matcher(tokenMatcher.group(2));
}
if (!firstMatch) {
throw new Exception(NLS.bind(Messages.Bad_value_list_0, values));
}
return result;
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public SmartArrayList() {
super();
}
/**
* Constructs a list containing the elements of the specified collection, in
* the order they are returned by the collection's iterator. The
* SmartArrayList instance has an initial capacity of 110% the size of the
* specified collection.
*
* @param c
* the collection whose elements are to be placed into this list
*/
public SmartArrayList(Collection<? extends E> c) {
super(c);
}
/**
* Constructs a list containing the result of the specified mapping applied
* on the elements of the specified list
*
* @param c
* The list whose data are to be mapped into this list
* @param m
* An instance of an ElementMapper
* @throws MappingException
*/
public <S> SmartArrayList(Collection<? extends S> c, ElementMapper<S, E> m) throws MappingException {
super(c.size());
E newElem;
for (S elem : c) {
if ((newElem = m.mapping(elem)) != null)
add(newElem);
}
}
/**
* Constructs a list containing the elements of the specified list of
* arguments, in the order they are specified. The SmartArrayList instance
* has an initial capacity of 110% the size of the specified collection.
*
* @param args
* the list of elements which are to be placed into this list
*/
public SmartArrayList(@SuppressWarnings("unchecked") E... args) {
super(Arrays.asList(args));
}
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity
* the initial capacity of the list
* @throws IllegalArgumentException
* if the specified initial capacity is negative
*/
public SmartArrayList(int initialCapacity) {
super(initialCapacity);
}
/**
* The same as equals but the lists does not have to have the same element
* order
*
* @param otherList
* @return
*/
public boolean hasEqualContent(SmartArrayList<E> otherList) {
for (E item : this) {
if (!otherList.contains(item)) {
return false;
}
}
for (E item : otherList) {
if (!this.contains(item)) {
return false;
}
}
return true;
}
/**
* Reverses the order of the SmartArrayList's elements.
*
* @return reversed SmartArrayList
*/
public SmartArrayList<E> reverse() {
Collections.reverse(this);
return this;
}
/**
* Rotates the elements of the SmartArrayList's elements by the specified
* distance.
*
* @param distance
* the distance to rotate the list. There are no constraints on
* this value.
* @return changed SmartArrayList
*/
public SmartArrayList<E> rotate(int distance) {
Collections.rotate(this, distance);
return this;
}
/**
* Randomly permutes the list using a default source of randomness.
*
* @return changed SmartArrayList
*/
public SmartArrayList<E> shuffle() {
Collections.shuffle(this);
return this;
}
/**
* Creates a string, which contains all values of an instance separated by
* ', '.
*
* @return a string containing all values of an instance separated by ', '
*/
@Override
public String toString() {
return toString(", "); //$NON-NLS-1$
}
/**
* Creates a string, which contains all values of an instance separated by
* specified delimeter.
*
* @param delimiter
* delimiter to separate the values
* @return a string containing all values of an instance separated by the
* specified delimiter
*/
public String toString(String delimiter) {
Object[] arr = toArray();
StringBuffer result = new StringBuffer();
for (int i = 0; i < arr.length; i++) {
result.append((i > 0 ? delimiter : "") + arr[i]); //$NON-NLS-1$
}
return result.toString();
}
}