package rabbitescape.engine.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import rabbitescape.engine.err.RabbitEscapeException;
public class Util
{
public static interface Function<T, R>
{
public R apply( T t );
}
public static class ReadingResourceFailed extends RabbitEscapeException
{
private static final long serialVersionUID = 1L;
public final String name;
public ReadingResourceFailed( Throwable cause, String name )
{
super( cause );
this.name = name;
}
public ReadingResourceFailed( String name )
{
this.name = name;
}
}
public static class MissingResource extends ReadingResourceFailed
{
private static final long serialVersionUID = 1L;
public MissingResource( String name )
{
super( name );
}
}
/**
* Always assert something, without needing -ea set on the JVM.
*/
public static void reAssert( boolean assertion )
{
if ( !assertion )
{
throw new AssertionError();
}
}
/**
* As reAssert( boolean assertion ), but with an explanation.
*/
public static void reAssert( boolean assertion, String message )
{
if ( !assertion )
{
throw new AssertionError( message );
}
}
public static <T> T getNth( Iterable<T> input, int n )
{
int i = 0;
for ( T item : input )
{
if ( i == n )
{
return item;
}
++i;
}
throw new ArrayIndexOutOfBoundsException( n );
}
public static <T, R> Iterable<R> map(
Function<T, R> function, Iterable<T> iterable )
{
List<R> ret = new ArrayList<>();
for ( T t : iterable )
{
ret.add( function.apply( t ) );
}
return ret;
}
public static <T, R> Iterable<R> map(
Function<T, R> function, T[] input )
{
List<R> ret = new ArrayList<>();
for ( T t : input )
{
ret.add( function.apply( t ) );
}
return ret;
}
public static <T, R> R[] map(
Function<T, R> function, T[] input, R[] retType )
{
List<R> ret = new ArrayList<>();
for ( T t : input )
{
ret.add( function.apply( t ) );
}
return ret.toArray( retType );
}
public static <T> List<T> list( Iterable<T> input )
{
List<T> ret = new ArrayList<>();
for ( T item : input )
{
ret.add( item );
}
return ret;
}
public static <T> List<T> list( T[] input )
{
return Arrays.asList( input );
}
public static <T> List<T> list( Iterator<T> input )
{
List<T> ret = new ArrayList<>();
while ( input.hasNext() )
{
ret.add( input.next() );
}
return ret;
}
public static <T> List<T> filterIn( Iterable<T> i, Class<? extends T> filter )
{
ArrayList<T> filtered = new ArrayList<T>();
for ( T o: i)
{
if ( filter.isInstance( o ) )
{
filtered.add( o );
}
}
return filtered;
}
public static <T> List<T> filterOut( Iterable<T> i, Class<? extends T> filter )
{
ArrayList<T> filtered = new ArrayList<T>();
for ( T o: i)
{
if ( !filter.isInstance( o ) )
{
filtered.add( o );
}
}
return filtered;
}
public static String[] stringArray( List<String> list )
{
return list.toArray( new String[list.size()] );
}
public static String[] stringArray( Iterable<String> items )
{
return stringArray( list( items ) );
}
public static Character[] characterArray( List<Character> list )
{
return list.toArray( new Character[list.size()] );
}
public static Character[] characterArray( Iterable<Character> items )
{
return characterArray( list( items ) );
}
public static Integer[] integerArray( List<Integer> list )
{
return list.toArray( new Integer[list.size()] );
}
public static Integer[] integerArray( Iterable<Integer> items )
{
return integerArray( list( items ) );
}
public static Map<String, Object> newMap( String... keysAndValues )
{
reAssert( keysAndValues.length % 2 == 0 );
Map<String, Object> ret = new HashMap<>();
for ( int i = 0; i < keysAndValues.length; i += 2 )
{
ret.put( keysAndValues[i], keysAndValues[i + 1] );
}
return ret;
}
@SuppressWarnings( "unchecked" )
public static <T> Set<T> newSet( T... items )
{
return new HashSet<>( Arrays.asList( items ) );
}
public static Iterable<Character> asChars( final String str )
{
class CharIterator implements Iterator<Character>
{
private final String str;
private final int strLength;
private int i = 0;
public CharIterator( String str )
{
this.str = str;
this.strLength = str.length();
this.i = -1;
}
@Override
public boolean hasNext()
{
return i < strLength - 1;
}
@Override
public Character next()
{
++i;
if ( i >= strLength )
{
throw new NoSuchElementException();
}
return str.charAt( i );
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
}
return new Iterable<Character>()
{
@Override
public Iterator<Character> iterator()
{
return new CharIterator( str );
}
};
}
public static String stringFromChars( Iterable<Character> chars )
{
StringBuilder ret = new StringBuilder();
for ( Character c : chars )
{
ret.append( c );
}
return ret.toString();
}
/**
* @brief Split a long string into lines
* @param s The string to split.
* @param maxChar The maximum requested line length.
* @return Each line is an element in the array.
* Lines will be split at spaces. Words will never be split.
*/
public static String[] wrap(String s, int maxChar)
{
// Match a bunch of anything apart from a space
Pattern p = Pattern.compile( "([^ ]+)" );
Matcher m = p.matcher( s );
ArrayList<String> al = new ArrayList<String>();
if ( !m.find() ) // No spaces to split on
{
return new String[] { s };
}
String line = m.group(1); // No space before first word
while ( m.find() )
{
String word = m.group(1);
int lineLength = line.length() + word.length() + 1;// + 1 for " "
if ( lineLength <= maxChar )
{
line = line + " " + word; // Replace space between words
}
else // Previous line is full
{
al.add( line ); // Store it
line = word; // and start the next one
}
}
al.add( line ); // Don't forget the last line
return al.toArray( new String[al.size()] );
}
/**
* As wrap(), but returns a single string with newlines.
*/
public static String wrapToNewline( String s, int maxChar)
{
return Util.join( "\n", wrap( s, maxChar ) );
}
public static String join( String glue, Object[] items )
{
return join( glue, Arrays.asList(items ));
}
public static String join( String glue, String[] items )
{
return join( glue, Arrays.asList( items ) );
}
public static <T> String join( String glue, Iterable<T> items )
{
StringBuilder ret = new StringBuilder();
boolean first = true;
for ( T item : items )
{
if ( first )
{
first = false;
}
else
{
ret.append( glue );
}
ret.append( item );
}
return ret.toString();
}
/**
* Split input by finding all occurrences of delimiter. Works like a
* sane version of String.split, but with no regular expressions.
*
* @param delimiter is not a regular expression, just a string.
*
* @return An array of strings that is always n + 1 long, where n is the
* number of times the string delimiter appears in input.
*/
public static String[] split( String input, String delimiter )
{
return split( input, delimiter, -1 );
}
/**
* Split input by finding all occurrences of delimiter. Works like a sane
* version of String.split, but with no regular expressions.
*
* @param delimiter is not a regular expression, just a string.
*
* @param maxSplits the maximum number of times the string should be split,
* or -1 for as many as possible.
*
* @return An array of strings that is always 1 + min(n, maxSplits)
* long, where n is the number of times the string delimiter
* appears in input.
*/
public static String[] split(
String input, String delimiter, int maxSplits )
{
List<String> ret = new ArrayList<String>();
int lastI = 0;
int i = input.indexOf( delimiter );
while ( i != -1 && ( maxSplits == -1 || ret.size() < maxSplits ) )
{
ret.add( input.substring( lastI, i ) );
lastI = i + delimiter.length();
i = input.indexOf( delimiter, lastI );
}
ret.add( input.substring( lastI ) );
return ret.toArray( new String[ret.size()] );
}
public static Iterable<Integer> range( final int max )
{
return new Iterable<Integer>()
{
@Override
public Iterator<Integer> iterator()
{
class MyIt implements Iterator<Integer>
{
private final int max;
private int i;
public MyIt( int max )
{
this.max = max;
}
@Override
public boolean hasNext()
{
return i < max;
}
@Override
public Integer next()
{
return i++;
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
}
return new MyIt( max );
}
};
}
/**
* This can be used to chain objects of different types (unlike concat). The returned
* Iterable will be of the first common superclass.
*/
@SafeVarargs
public static <T> Iterable<T> chain( final Iterable<? extends T>... itArray )
{
return new Iterable<T>()
{
@Override
public Iterator<T> iterator()
{
class MyIt implements Iterator<T>
{
/** Iterator of Iterators */
Iterator<Iterator<? extends T>> iI;
/** The current sub-Iterator */
Iterator<? extends T> i;
public MyIt( Iterator<Iterator<? extends T>> iI )
{
this.iI = iI;
i = iI.next();
}
@Override
public boolean hasNext()
{
if ( i.hasNext() )
{
return true;
}
else
{
if ( iI.hasNext() )
{
i = iI.next();
return this.hasNext();
}
else
{
return false;
}
}
}
@Override
public T next()
{
if ( i.hasNext() )
{
return i.next();
}
else
{
i = iI.next();
return this.next();
}
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
}
List<Iterator<? extends T>> newIL = new ArrayList<Iterator<? extends T>>();
for ( Iterable<? extends T> it: itArray )
{
newIL.add( it.iterator() );
}
return new MyIt(newIL.iterator());
}
};
}
private static abstract class UntilNullIterator<T> implements Iterator<T>
{
private T nextItem;
private boolean start;
public UntilNullIterator()
{
this.nextItem = null;
this.start = true;
}
@Override
public boolean hasNext()
{
if ( start ) advance();
return nextItem != null;
}
@Override
public T next()
{
if ( start ) advance();
T item = nextItem;
advance();
return item;
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
private void advance()
{
start = false;
nextItem = nextOrNull();
}
protected abstract T nextOrNull();
}
public static <T> Iterable<T> filter(
final Function<T, Boolean> predicate,
final Iterable<T> input
)
{
class It extends UntilNullIterator<T>
{
private final Function<T, Boolean> predicate;
private final Iterator<T> it;
public It( Function<T, Boolean> predicate, Iterator<T> it )
{
this.predicate = predicate;
this.it = it;
}
@Override
protected T nextOrNull()
{
T item = null;
while ( it.hasNext() )
{
T tmp = it.next();
if ( predicate.apply( tmp ) )
{
item = tmp;
break;
}
}
return item;
}
}
return new Iterable<T>()
{
@Override
public Iterator<T> iterator()
{
return new It( predicate, input.iterator() );
}
};
}
public static Function<String, String> stripLast( final int i )
{
return new Function<String, String>()
{
@Override
public String apply( String input )
{
return input.substring( 0, Math.max( 0, input.length() - i ) );
}
};
}
public static Function<String, Boolean> endsWith( final String suffix )
{
return new Function<String, Boolean>()
{
@Override
public Boolean apply( String input )
{
return input.endsWith( suffix );
}
};
}
public static <T> Iterable<T> sorted( Iterable<T> input )
{
TreeSet<T> ret = new TreeSet<T>();
for ( T t : input )
{
ret.add( t );
}
return ret;
}
private static class ReaderIterator extends UntilNullIterator<String>
{
private final String name;
private final BufferedReader reader;
public ReaderIterator( String name, BufferedReader reader )
{
this.name = name;
this.reader = reader;
}
@Override
protected String nextOrNull()
{
try
{
return reader.readLine();
}
catch ( IOException e )
{
throw new ReadingResourceFailed( e, name );
}
}
}
public static Iterable<String> streamLines( InputStream input )
{
return streamLines( null, input );
}
private static Iterable<String> streamLines(
final String name, final InputStream input )
{
return new Iterable<String>()
{
@Override
public Iterator<String> iterator()
{
return new ReaderIterator(
name, new BufferedReader( new InputStreamReader( input ) ) );
}
};
}
public static Iterable<String> resourceLines( String name )
{
InputStream res = Util.class.getResourceAsStream( name );
if ( res == null )
{
throw new MissingResource( name );
}
return streamLines( name, res );
}
public static <T> T[] concat( T[] a, T[] b, T[] c, T[] d )
{
// Someone tell us how to make this better.
return list(
chain(
list( a )
, list( b )
, list( c )
, list( d )
)
).toArray( a );
}
public static <T> T[] concat( T[] a, T[] b, T[] c )
{
return list(
chain(
list( a )
, list( b )
, list( c )
)
).toArray( a );
}
/**
* Use chain instead for arguments of different classes.
*/
public static <T> T[] concat( T[] a, T[] b )
{
return list(
chain(
list( a )
, list( b )
)
).toArray( a );
}
public static <T> boolean equalsOrBothNull( T left, T right )
{
if ( left == null )
{
return ( right == null );
}
else
{
return left.equals( right );
}
}
/**
* Use in preference to String.isEmpty since that is not available
* in Android 2.2.
*/
public static boolean isEmpty( String str )
{
return ( str.length() == 0 );
}
public static <T> List<String> toStringList( T[] values )
{
ArrayList<String> ret = new ArrayList<String>();
for( T t : values )
{
ret.add( t.toString() );
}
return ret;
}
public static class IdxObj<T>
{
public static <T> IdxObj<T> make( int index, T object )
{
return new IdxObj<T>( index, object );
}
public final int index;
public final T object;
public IdxObj( int index, T object )
{
if ( object == null )
{
throw new NullPointerException();
}
this.index = index;
this.object = object;
}
@Override
public int hashCode()
{
return ( 31 * index ) + object.hashCode();
}
@Override
public boolean equals( Object otherObj )
{
if ( ! ( otherObj instanceof IdxObj ) )
{
return false;
}
@SuppressWarnings( "unchecked" )
IdxObj<T> other = (IdxObj<T>)otherObj;
return (
index == other.index
&& object.equals( other.object )
);
}
@Override
public String toString()
{
return "IdxObj( " + index + ", " + object + " )";
}
}
public static <T> Iterable<IdxObj<T>> enumerate1( final T[] array )
{
return enumerate1( Arrays.asList( array ) );
}
public static <T> Iterable<IdxObj<T>> enumerate( final T[] array )
{
return enumerate( Arrays.asList( array ) );
}
public static <T> Iterable<IdxObj<T>> enumerate1( final Iterable<T> i )
{
return enumerateN( i, 1 );
}
public static <T> Iterable<IdxObj<T>> enumerate( final Iterable<T> i )
{
return enumerateN( i, 0 );
}
private static <T> Iterable<IdxObj<T>> enumerateN(
final Iterable<T> i, final int startAtIndex )
{
return new Iterable<IdxObj<T>>()
{
@Override
public Iterator<IdxObj<T>> iterator()
{
return new Iterator<IdxObj<T>>()
{
private final Iterator<T> it = i.iterator();
private int index = startAtIndex - 1;
@Override
public boolean hasNext()
{
return it.hasNext();
}
@Override
public IdxObj<T> next()
{
// If next throws, we don't update index either.
T n = it.next();
++index;
return IdxObj.make( index, n );
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
};
}
/**
* @brief Replace all matches, but preserves the first capturing group.
* Compare to String.replaceAll, which replaces the whole match.
* @param patternFlags The flags from java.util.regex.Pattern.
*/
public static String regexRemovePreserveGroup( String s, String regex, int patternFlags )
{
Pattern p = Pattern.compile( regex, patternFlags );
Matcher dsMatcher = p.matcher( s );
StringBuffer sb = new StringBuffer();
while ( dsMatcher.find() )
{
dsMatcher.appendReplacement( sb, "$1" );
}
dsMatcher.appendTail( sb );
return sb.toString();
}
public static String regexRemovePreserveGroup( String s, String regex )
{
return regexRemovePreserveGroup( s, regex, 0 );
}
public static String regexReplace( String s, String regex, String replacement )
{
Pattern p = Pattern.compile( regex );
Matcher dsMatcher = p.matcher( s );
StringBuffer sb = new StringBuffer();
while ( dsMatcher.find() )
{
dsMatcher.appendReplacement( sb, replacement );
}
dsMatcher.appendTail( sb );
return sb.toString();
}
}