pax_global_header 0000666 0000000 0000000 00000000064 11660305702 0014512 g ustar 00root root 0000000 0000000 52 comment=332db9fdd0340d9921483dc310cb3af4f9fcd23f
plexus-utils2-2.0.5/ 0000775 0000000 0000000 00000000000 11660305702 0014256 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/NOTICE.txt 0000664 0000000 0000000 00000000145 11660305702 0016000 0 ustar 00root root 0000000 0000000 This product includes software developed by
The Apache Software Foundation (http://www.apache.org/).
plexus-utils2-2.0.5/pom.xml 0000664 0000000 0000000 00000006256 11660305702 0015604 0 ustar 00root root 0000000 0000000
* This is not a general purpose test and should only be used if you
* can live with false positives. For example, pattern=**\a
* and str=b
will yield true
.
*
* @param pattern The pattern to match against. Must not be
* null
.
* @param str The path to match, as a String. Must not be
* null
.
*
* @return whether or not a given path matches the start of a given
* pattern up to the first "**".
*/
protected static boolean matchPatternStart( String pattern, String str )
{
return SelectorUtils.matchPatternStart( pattern, str );
}
/**
* Tests whether or not a given path matches the start of a given
* pattern up to the first "**".
*
* This is not a general purpose test and should only be used if you
* can live with false positives. For example, pattern=**\a
* and str=b
will yield true
.
*
* @param pattern The pattern to match against. Must not be
* null
.
* @param str The path to match, as a String. Must not be
* null
.
* @param isCaseSensitive Whether or not matching should be performed
* case sensitively.
*
* @return whether or not a given path matches the start of a given
* pattern up to the first "**".
*/
protected static boolean matchPatternStart( String pattern, String str,
boolean isCaseSensitive )
{
return SelectorUtils.matchPatternStart( pattern, str, isCaseSensitive );
}
/**
* Tests whether or not a given path matches a given pattern.
*
* @param pattern The pattern to match against. Must not be
* null
.
* @param str The path to match, as a String. Must not be
* null
.
*
* @return true
if the pattern matches against the string,
* or false
otherwise.
*/
protected static boolean matchPath( String pattern, String str )
{
return SelectorUtils.matchPath( pattern, str );
}
/**
* Tests whether or not a given path matches a given pattern.
*
* @param pattern The pattern to match against. Must not be
* null
.
* @param str The path to match, as a String. Must not be
* null
.
* @param isCaseSensitive Whether or not matching should be performed
* case sensitively.
*
* @return true
if the pattern matches against the string,
* or false
otherwise.
*/
protected static boolean matchPath( String pattern, String str,
boolean isCaseSensitive )
{
return SelectorUtils.matchPath( pattern, str, isCaseSensitive );
}
/**
* Tests whether or not a string matches against a pattern.
* The pattern may contain two special characters:
* '*' means zero or more characters
* '?' means one and only one character
*
* @param pattern The pattern to match against.
* Must not be null
.
* @param str The string which must be matched against the pattern.
* Must not be null
.
*
* @return true
if the string matches against the pattern,
* or false
otherwise.
*/
public static boolean match( String pattern, String str )
{
return SelectorUtils.match( pattern, str );
}
/**
* Tests whether or not a string matches against a pattern.
* The pattern may contain two special characters:
* '*' means zero or more characters
* '?' means one and only one character
*
* @param pattern The pattern to match against.
* Must not be null
.
* @param str The string which must be matched against the pattern.
* Must not be null
.
* @param isCaseSensitive Whether or not matching should be performed
* case sensitively.
*
*
* @return true
if the string matches against the pattern,
* or false
otherwise.
*/
protected static boolean match( String pattern, String str,
boolean isCaseSensitive )
{
return SelectorUtils.match( pattern, str, isCaseSensitive );
}
/**
* Sets the list of include patterns to use. All '/' and '\' characters
* are replaced by File.separatorChar
, so the separator used
* need not match File.separatorChar
.
*
* When a pattern ends with a '/' or '\', "**" is appended.
*
* @param includes A list of include patterns.
* May be null
, indicating that all files
* should be included. If a non-null
* list is given, all elements must be
* non-null
.
*/
public void setIncludes( String[] includes )
{
if ( includes == null )
{
this.includes = null;
}
else
{
this.includes = new String[includes.length];
for ( int i = 0; i < includes.length; i++ )
{
this.includes[i] = normalizePattern( includes[i] );
}
}
}
/**
* Sets the list of exclude patterns to use. All '/' and '\' characters
* are replaced by File.separatorChar
, so the separator used
* need not match File.separatorChar
.
*
* When a pattern ends with a '/' or '\', "**" is appended.
*
* @param excludes A list of exclude patterns.
* May be null
, indicating that no files
* should be excluded. If a non-null
list is
* given, all elements must be non-null
.
*/
public void setExcludes( String[] excludes )
{
if ( excludes == null )
{
this.excludes = null;
}
else
{
this.excludes = new String[excludes.length];
for ( int i = 0; i < excludes.length; i++ )
{
this.excludes[i] = normalizePattern( excludes[i] );
}
}
}
/**
* Normalizes the pattern, e.g. converts forward and backward slashes to the platform-specific file separator.
*
* @param pattern The pattern to normalize, must not be null
.
* @return The normalized pattern, never null
.
*/
private String normalizePattern( String pattern )
{
pattern = pattern.trim();
if ( pattern.startsWith( SelectorUtils.REGEX_HANDLER_PREFIX ) )
{
if ( File.separatorChar == '\\' )
{
pattern = StringUtils.replace( pattern, "/", "\\\\" );
}
else
{
pattern = StringUtils.replace( pattern, "\\\\", "/" );
}
}
else
{
pattern = pattern.replace( File.separatorChar == '/' ? '\\' : '/', File.separatorChar );
if ( pattern.endsWith( File.separator ) )
{
pattern += "**";
}
}
return pattern;
}
/**
* Tests whether or not a name matches against at least one include
* pattern.
*
* @param name The name to match. Must not be null
.
* @return true
when the name matches against at least one
* include pattern, or false
otherwise.
*/
protected boolean isIncluded( String name )
{
for ( int i = 0; i < includes.length; i++ )
{
if ( matchPath( includes[i], name, isCaseSensitive ) )
{
return true;
}
}
return false;
}
/**
* Tests whether or not a name matches the start of at least one include
* pattern.
*
* @param name The name to match. Must not be null
.
* @return true
when the name matches against the start of at
* least one include pattern, or false
otherwise.
*/
protected boolean couldHoldIncluded( String name )
{
for ( int i = 0; i < includes.length; i++ )
{
if ( matchPatternStart( includes[i], name, isCaseSensitive ) )
{
return true;
}
}
return false;
}
/**
* Tests whether or not a name matches against at least one exclude
* pattern.
*
* @param name The name to match. Must not be null
.
* @return true
when the name matches against at least one
* exclude pattern, or false
otherwise.
*/
protected boolean isExcluded( String name )
{
for ( int i = 0; i < excludes.length; i++ )
{
if ( matchPath( excludes[i], name, isCaseSensitive ) )
{
return true;
}
}
return false;
}
/**
* Adds default exclusions to the current exclusions set.
*/
public void addDefaultExcludes()
{
int excludesLength = excludes == null ? 0 : excludes.length;
String[] newExcludes;
newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length];
if ( excludesLength > 0 )
{
System.arraycopy( excludes, 0, newExcludes, 0, excludesLength );
}
for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ )
{
newExcludes[i + excludesLength] = DEFAULTEXCLUDES[i].replace( '/', File.separatorChar );
}
excludes = newExcludes;
}
protected void setupDefaultFilters()
{
if ( includes == null )
{
// No includes supplied, so set it to 'matches all'
includes = new String[1];
includes[0] = "**";
}
if ( excludes == null )
{
excludes = new String[0];
}
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/Base64.java 0000664 0000000 0000000 00000040701 11660305702 0025462 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
/**
* Provides Base64 encoding and decoding as defined by RFC 2045.
*
*
This class implements section 6.8. Base64 Content-Transfer-Encoding * from RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: * Format of Internet Message Bodies by Freed and Borenstein.
* * @see RFC 2045 * @author Apache Software Foundation * @since 1.0-dev * @version $Id: Base64.java 8010 2009-01-07 12:59:50Z vsiveton $ */ public class Base64 { // // Source Id: Base64.java 161350 2005-04-14 20:39:46Z ggregory // /** * Chunk size per RFC 2045 section 6.8. * *The {@value} character limit does not count the trailing CRLF, but counts * all other characters, including any equal signs.
* * @see RFC 2045 section 6.8 */ static final int CHUNK_SIZE = 76; /** * Chunk separator per RFC 2045 section 2.1. * * @see RFC 2045 section 2.1 */ static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes(); /** * The base length. */ static final int BASELENGTH = 255; /** * Lookup length. */ static final int LOOKUPLENGTH = 64; /** * Used to calculate the number of bits in a byte. */ static final int EIGHTBIT = 8; /** * Used when encoding something which has fewer than 24 bits. */ static final int SIXTEENBIT = 16; /** * Used to determine how many bits data contains. */ static final int TWENTYFOURBITGROUP = 24; /** * Used to get the number of Quadruples. */ static final int FOURBYTE = 4; /** * Used to test the sign of a byte. */ static final int SIGN = -128; /** * Byte used to pad output. */ static final byte PAD = (byte) '='; /** * Contains the Base64 values0
through 63
accessed by using character encodings as
* indices.
*
* For example, base64Alphabet['+']
returns 62
.
*
* The value of undefined encodings is -1
.
*
* Contains the Base64 encodings A
through Z
, followed by a
through
* z
, followed by 0
through 9
, followed by +
, and
* /
.
*
* This array is accessed by using character values as indices. *
*
* For example, lookUpBase64Alphabet[62]
returns '+'
.
*
octect
is in the base 64 alphabet.
*
* @param octect The value to test
* @return true
if the value is defined in the the base 64 alphabet, false
otherwise.
*/
private static boolean isBase64(byte octect) {
if (octect == PAD) {
return true;
} else if (octect < 0 || base64Alphabet[octect] == -1) {
return false;
} else {
return true;
}
}
/**
* Tests a given byte array to see if it contains
* only valid characters within the Base64 alphabet.
*
* @param arrayOctect byte array to test
* @return true
if all bytes are valid characters in the Base64
* alphabet or if the byte array is empty; false, otherwise
*/
public static boolean isArrayByteBase64(byte[] arrayOctect) {
arrayOctect = discardWhitespace(arrayOctect);
int length = arrayOctect.length;
if (length == 0) {
// shouldn't a 0 length array be valid base64 data?
// return false;
return true;
}
for (int i = 0; i < length; i++) {
if (!isBase64(arrayOctect[i])) {
return false;
}
}
return true;
}
/**
* Encodes binary data using the base64 algorithm but
* does not chunk the output.
*
* @param binaryData binary data to encode
* @return Base64 characters
*/
public static byte[] encodeBase64(byte[] binaryData) {
return encodeBase64(binaryData, false);
}
/**
* Encodes binary data using the base64 algorithm and chunks
* the encoded output into 76 character blocks
*
* @param binaryData binary data to encode
* @return Base64 characters chunked in 76 character blocks
*/
public static byte[] encodeBase64Chunked(byte[] binaryData) {
return encodeBase64(binaryData, true);
}
/**
* Decodes a byte[] containing containing
* characters in the Base64 alphabet.
*
* @param pArray A byte array containing Base64 character data
* @return a byte array containing binary data
*/
public byte[] decode(byte[] pArray) {
return decodeBase64(pArray);
}
/**
* Encodes binary data using the base64 algorithm, optionally
* chunking the output into 76 character blocks.
*
* @param binaryData Array containing binary data to encode.
* @param isChunked if true
this encoder will chunk
* the base64 output into 76 character blocks
* @return Base64-encoded data.
*/
public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
int lengthDataBits = binaryData.length * EIGHTBIT;
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
byte encodedData[] = null;
int encodedDataLength = 0;
int nbrChunks = 0;
if (fewerThan24bits != 0) {
//data not divisible by 24 bit
encodedDataLength = (numberTriplets + 1) * 4;
} else {
// 16 or 8 bit
encodedDataLength = numberTriplets * 4;
}
// If the output is to be "chunked" into 76 character sections,
// for compliance with RFC 2045 MIME, then it is important to
// allow for extra length to account for the separator(s)
if (isChunked) {
nbrChunks =
(CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE));
encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
}
encodedData = new byte[encodedDataLength];
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
int encodedIndex = 0;
int dataIndex = 0;
int i = 0;
int nextSeparatorIndex = CHUNK_SIZE;
int chunksSoFar = 0;
//log.debug("number of triplets = " + numberTriplets);
for (i = 0; i < numberTriplets; i++) {
dataIndex = i * 3;
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
b3 = binaryData[dataIndex + 2];
//log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 =
((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 =
((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
byte val3 =
((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
//log.debug( "val2 = " + val2 );
//log.debug( "k4 = " + (k<<4) );
//log.debug( "vak = " + (val2 | (k<<4)) );
encodedData[encodedIndex + 1] =
lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex + 2] =
lookUpBase64Alphabet[(l << 2) | val3];
encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
encodedIndex += 4;
// If we are chunking, let's put a chunk separator down.
if (isChunked) {
// this assumes that CHUNK_SIZE % 4 == 0
if (encodedIndex == nextSeparatorIndex) {
System.arraycopy(
CHUNK_SEPARATOR,
0,
encodedData,
encodedIndex,
CHUNK_SEPARATOR.length);
chunksSoFar++;
nextSeparatorIndex =
(CHUNK_SIZE * (chunksSoFar + 1)) +
(chunksSoFar * CHUNK_SEPARATOR.length);
encodedIndex += CHUNK_SEPARATOR.length;
}
}
}
// form integral number of 6-bit groups
dataIndex = i * 3;
if (fewerThan24bits == EIGHTBIT) {
b1 = binaryData[dataIndex];
k = (byte) (b1 & 0x03);
//log.debug("b1=" + b1);
//log.debug("b1<<2 = " + (b1>>2) );
byte val1 =
((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
encodedData[encodedIndex + 2] = PAD;
encodedData[encodedIndex + 3] = PAD;
} else if (fewerThan24bits == SIXTEENBIT) {
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 =
((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 =
((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex + 1] =
lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
encodedData[encodedIndex + 3] = PAD;
}
if (isChunked) {
// we also add a separator to the end of the final chunk.
if (chunksSoFar < nbrChunks) {
System.arraycopy(
CHUNK_SEPARATOR,
0,
encodedData,
encodedDataLength - CHUNK_SEPARATOR.length,
CHUNK_SEPARATOR.length);
}
}
return encodedData;
}
/**
* Decodes Base64 data into octects
*
* @param base64Data Byte array containing Base64 data
* @return Array containing decoded data.
*/
public static byte[] decodeBase64(byte[] base64Data) {
// RFC 2045 requires that we discard ALL non-Base64 characters
base64Data = discardNonBase64(base64Data);
// handle the edge case, so we don't have to worry about it later
if (base64Data.length == 0) {
return new byte[0];
}
int numberQuadruple = base64Data.length / FOURBYTE;
byte decodedData[] = null;
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
// Throw away anything not in base64Data
int encodedIndex = 0;
int dataIndex = 0;
{
// this sizes the output array properly - rlw
int lastData = base64Data.length;
// ignore the '=' padding
while (base64Data[lastData - 1] == PAD) {
if (--lastData == 0) {
return new byte[0];
}
}
decodedData = new byte[lastData - numberQuadruple];
}
for (int i = 0; i < numberQuadruple; i++) {
dataIndex = i * 4;
marker0 = base64Data[dataIndex + 2];
marker1 = base64Data[dataIndex + 3];
b1 = base64Alphabet[base64Data[dataIndex]];
b2 = base64Alphabet[base64Data[dataIndex + 1]];
if (marker0 != PAD && marker1 != PAD) {
//No PAD e.g 3cQl
b3 = base64Alphabet[marker0];
b4 = base64Alphabet[marker1];
decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex + 1] =
(byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
} else if (marker0 == PAD) {
//Two PAD e.g. 3c[Pad][Pad]
decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
} else if (marker1 == PAD) {
//One PAD e.g. 3cQ[Pad]
b3 = base64Alphabet[marker0];
decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex + 1] =
(byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
}
encodedIndex += 3;
}
return decodedData;
}
/**
* Discards any whitespace from a base-64 encoded block.
*
* @param data The base-64 encoded data to discard the whitespace
* from.
* @return The data, less whitespace (see RFC 2045).
*/
static byte[] discardWhitespace(byte[] data) {
byte groomedData[] = new byte[data.length];
int bytesCopied = 0;
for (int i = 0; i < data.length; i++) {
switch (data[i]) {
case (byte) ' ' :
case (byte) '\n' :
case (byte) '\r' :
case (byte) '\t' :
break;
default:
groomedData[bytesCopied++] = data[i];
}
}
byte packedData[] = new byte[bytesCopied];
System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
return packedData;
}
/**
* Discards any characters outside of the base64 alphabet, per
* the requirements on page 25 of RFC 2045 - "Any characters
* outside of the base64 alphabet are to be ignored in base64
* encoded data."
*
* @param data The base-64 encoded data to groom
* @return The data, less non-base64 characters (see RFC 2045).
*/
static byte[] discardNonBase64(byte[] data) {
byte groomedData[] = new byte[data.length];
int bytesCopied = 0;
for (int i = 0; i < data.length; i++) {
if (isBase64(data[i])) {
groomedData[bytesCopied++] = data[i];
}
}
byte packedData[] = new byte[bytesCopied];
System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
return packedData;
}
/**
* Encodes a byte[] containing binary data, into a byte[] containing
* characters in the Base64 alphabet.
*
* @param pArray a byte array containing binary data
* @return A byte array containing only Base64 character data
*/
public byte[] encode(byte[] pArray) {
return encodeBase64(pArray, false);
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/CachedMap.java 0000664 0000000 0000000 00000034047 11660305702 0026251 0 ustar 00root root 0000000 0000000 /*
* J.A.D.E. Java(TM) Addition to Default Environment.
* Latest release available at http://jade.dautelle.com/
* This class is public domain (not copyrighted).
*/
package org.codehaus.plexus.util;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* This class provides cache access to Map
collections.
Instance of this class can be used as "proxy" for any collection
* implementing the java.util.Map
interface.
Typically, {@link CachedMap} are used to accelerate access to
* large collections when the access to the collection is not evenly
* distributed (associative cache). The performance gain is about
* 50% for the fastest hash map collections (e.g. {@link FastMap}).
* For slower collections such as java.util.TreeMap
,
* non-resizable {@link FastMap} (real-time) or database access,
* performance can be of several orders of magnitude.
Note: The keys used to access elements of a {@link CachedMap} do
* not need to be immutable as they are not stored in the cache
* (only keys specified by the {@link #put} method are).
* In other words, access can be performed using mutable keys as long
* as these keys can be compared for equality with the real map's keys
* (e.g. same hashCode
values).
This implementation is not synchronized. Multiple threads accessing * or modifying the collection must be synchronized externally.
* *This class is public domain (not copyrighted).
* * @author Jean-Marie Dautelle * @version 5.3, October 30, 2003 */ public final class CachedMap implements Map { /** * Holds the FastMap backing this collection * (null
if generic backing map).
*/
private final FastMap _backingFastMap;
/**
* Holds the generic map backing this collection.
*/
private final Map _backingMap;
/**
* Holds the keys of the backing map (key-to-key mapping).
* (null
if FastMap backing map).
*/
private final FastMap _keysMap;
/**
* Holds the cache's mask (capacity - 1).
*/
private final int _mask;
/**
* Holds the keys being cached.
*/
private final Object[] _keys;
/**
* Holds the values being cached.
*/
private final Object[] _values;
/**
* Creates a cached map backed by a {@link FastMap}.
* The default cache size and map capacity is set to 256
* entries.
*/
public CachedMap() {
this(256, new FastMap());
}
/**
* Creates a cached map backed by a {@link FastMap} and having the
* specified cache size.
*
* @param cacheSize the cache size, the actual cache size is the
* first power of 2 greater or equal to cacheSize
.
* This is also the initial capacity of the backing map.
*/
public CachedMap(int cacheSize) {
this(cacheSize, new FastMap(cacheSize));
}
/**
* Creates a cached map backed by the specified map and having the specified
* cache size. In order to maitain cache veracity, it is critical
* that all update to the backing map is accomplished through the
* {@link CachedMap} instance; otherwise {@link #flush} has to be called.
*
* @param cacheSize the cache size, the actual cache size is the
* first power of 2 greater or equal to cacheSize
.
* @param backingMap the backing map to be "wrapped" in a cached map.
*/
public CachedMap(int cacheSize, Map backingMap) {
// Find a power of 2 >= minimalCache
int actualCacheSize = 1;
while (actualCacheSize < cacheSize) {
actualCacheSize <<= 1;
}
// Sets up cache.
_keys = new Object[actualCacheSize];
_values = new Object[actualCacheSize];
_mask = actualCacheSize - 1;
// Sets backing map references.
if (backingMap instanceof FastMap) {
_backingFastMap = (FastMap)backingMap;
_backingMap = _backingFastMap;
_keysMap = null;
} else {
_backingFastMap = null;
_backingMap = backingMap;
_keysMap = new FastMap(backingMap.size());
for (Iterator i= backingMap.keySet().iterator(); i.hasNext();) {
Object key = i.next();
_keysMap.put(key, key);
}
}
}
/**
* Returns the actual cache size.
*
* @return the cache size (power of 2).
*/
public int getCacheSize() {
return _keys.length;
}
/**
* Returns the backing map. If the backing map is modified directly,
* this {@link CachedMap} has to be flushed.
*
* @return the backing map.
* @see #flush
*/
public Map getBackingMap() {
return (_backingFastMap != null) ? _backingFastMap : _backingMap;
}
/**
* Flushes the key/value pairs being cached. This method should be called
* if the backing map is externally modified.
*/
public void flush() {
for (int i=0; i < _keys.length; i++) {
_keys[i] = null;
_values[i] = null;
}
if (_keysMap != null) {
// Re-populates keys from backing map.
for (Iterator i= _backingMap.keySet().iterator(); i.hasNext();) {
Object key = i.next();
_keysMap.put(key, key);
}
}
}
/**
* Returns the value to which this map maps the specified key.
* First, the cache is being checked, then if the cache does not contains
* the specified key, the backing map is accessed and the key/value
* pair is stored in the cache.
*
* @param key the key whose associated value is to be returned.
* @return the value to which this map maps the specified key, or
* null
if the map contains no mapping for this key.
* @throws ClassCastException if the key is of an inappropriate type for
* the backing map (optional).
* @throws NullPointerException if the key is null
.
*/
public Object get(Object key) {
int index = key.hashCode() & _mask;
return key.equals(_keys[index]) ?
_values[index] : getCacheMissed(key, index);
}
private Object getCacheMissed(Object key, int index) {
if (_backingFastMap != null) {
Map.Entry entry = _backingFastMap.getEntry(key);
if (entry != null) {
_keys[index] = entry.getKey();
Object value = entry.getValue();
_values[index] = value;
return value;
} else {
return null;
}
} else { // Generic backing map.
Object mapKey = _keysMap.get(key);
if (mapKey != null) {
_keys[index] = mapKey;
Object value = _backingMap.get(key);
_values[index] = value;
return value;
} else {
return null;
}
}
}
/**
* Associates the specified value with the specified key in this map.
*
* @param key the key with which the specified value is to be associated.
* @param value the value to be associated with the specified key.
* @return the previous value associated with specified key, or
* null
if there was no mapping for the key.
* @throws UnsupportedOperationException if the put
operation
* is not supported by the backing map.
* @throws ClassCastException if the class of the specified key or value
* prevents it from being stored in this map.
* @throws IllegalArgumentException if some aspect of this key or value
* prevents it from being stored in this map.
* @throws NullPointerException if the key is null
.
*/
public Object put(Object key, Object value) {
// Updates the cache.
int index = key.hashCode() & _mask;
if (key.equals(_keys[index]) ) {
_values[index] = value;
} else if (_keysMap != null) { // Possibly a new key.
_keysMap.put(key, key);
}
// Updates the backing map.
return _backingMap.put(key, value);
}
/**
* Removes the mapping for this key from this map if it is present.
*
* @param key key whose mapping is to be removed from the map.
* @return previous value associated with specified key,
* or null
if there was no mapping for key.
* @throws ClassCastException if the key is of an inappropriate type for
* the backing map (optional).
* @throws NullPointerException if the key is null
.
* @throws UnsupportedOperationException if the remove
method
* is not supported by the backing map.
*/
public Object remove(Object key) {
// Removes from cache.
int index = key.hashCode() & _mask;
if (key.equals(_keys[index]) ) {
_keys[index] = null;
}
// Removes from key map.
if (_keysMap != null) {
_keysMap.remove(key);
}
// Removes from backing map.
return _backingMap.remove(key);
}
/**
* Indicates if this map contains a mapping for the specified key.
*
* @param key the key whose presence in this map is to be tested.
* @return true
if this map contains a mapping for the
* specified key; false
otherwise.
*/
public boolean containsKey(Object key) {
// Checks the cache.
int index = key.hashCode() & _mask;
if (key.equals(_keys[index]) ) {
return true;
} else { // Checks the backing map.
return _backingMap.containsKey(key);
}
}
/**
* Returns the number of key-value mappings in this map. If the
* map contains more than Integer.MAX_VALUE
elements,
* returns Integer.MAX_VALUE
.
*
* @return the number of key-value mappings in this map.
*/
public int size() {
return _backingMap.size();
}
/**
* Returns true
if this map contains no key-value mappings.
*
* @return true
if this map contains no key-value mappings.
*/
public boolean isEmpty() {
return _backingMap.isEmpty();
}
/**
* Returns true
if this map maps one or more keys to the
* specified value.
*
* @param value value whose presence in this map is to be tested.
* @return true
if this map maps one or more keys to the
* specified value.
* @throws ClassCastException if the value is of an inappropriate type for
* the backing map (optional).
* @throws NullPointerException if the value is null
and the
* backing map does not not permit null
values.
*/
public boolean containsValue(Object value) {
return _backingMap.containsValue(value);
}
/**
* Copies all of the mappings from the specified map to this map
* (optional operation). This method automatically flushes the cache.
*
* @param map the mappings to be stored in this map.
* @throws UnsupportedOperationException if the putAll
method
* is not supported by the backing map.
* @throws ClassCastException if the class of a key or value in the
* specified map prevents it from being stored in this map.
* @throws IllegalArgumentException some aspect of a key or value in the
* specified map prevents it from being stored in this map.
* @throws NullPointerException the specified map is null
, or
* if the backing map does not permit null
keys or
* values, and the specified map contains null
keys or
* values.
*/
public void putAll(Map map) {
_backingMap.putAll(map);
flush();
}
/**
* Removes all mappings from this map (optional operation). This method
* automatically flushes the cache.
*
* @throws UnsupportedOperationException if clear is not supported by the
* backing map.
*/
public void clear() {
_backingMap.clear();
flush();
}
/**
* Returns an unmodifiable view of the keys contained in this
* map.
*
* @return an unmodifiable view of the keys contained in this map.
*/
public Set keySet() {
return Collections.unmodifiableSet(_backingMap.keySet());
}
/**
* Returns an unmodifiable view of the values contained in this map.
*
* @return an unmodifiable view of the values contained in this map.
*/
public Collection values() {
return Collections.unmodifiableCollection(_backingMap.values());
}
/**
* Returns an unmodifiable view of the mappings contained in this
* map. Each element in the returned set is a Map.Entry
.
*
* @return an unmodifiable view of the mappings contained in this map.
*/
public Set entrySet() {
return Collections.unmodifiableSet(_backingMap.entrySet());
}
/**
* Compares the specified object with this map for equality. Returns
* true if the given object is also a map and the two Maps
* represent the same mappings.
*
* @param o object to be compared for equality with this map.
* @return true
if the specified object is equal to this map.
*/
public boolean equals(Object o) {
return _backingMap.equals(o);
}
/**
* Returns the hash code value for this map.
*
* @return the hash code value for this map.
*/
public int hashCode() {
return _backingMap.hashCode();
}
} plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/CollectionUtils.java 0000664 0000000 0000000 00000016746 11660305702 0027566 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.util.ArrayList;
import java.util.Collection;
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;
/**
* @author olamy
* @version $Id: CollectionUtils.java 8055 2009-01-16 12:45:08Z vsiveton $
*/
public class CollectionUtils
{
// ----------------------------------------------------------------------
// Static methods that can probably be moved to a real util class.
// ----------------------------------------------------------------------
/**
* Take a dominant and recessive Map and merge the key:value
* pairs where the recessive Map may add key:value pairs to the dominant
* Map but may not override any existing key:value pairs.
*
* If we have two Maps, a dominant and recessive, and
* their respective keys are as follows:
*
* dominantMapKeys = { a, b, c, d, e, f }
* recessiveMapKeys = { a, b, c, x, y, z }
*
* Then the result should be the following:
*
* resultantKeys = { a, b, c, d, e, f, x, y, z }
*
* @param dominantMap Dominant Map.
* @param recessiveMap Recessive Map.
* @return The result map with combined dominant and recessive values.
*/
public static Map mergeMaps( Map dominantMap, Map recessiveMap )
{
if ( dominantMap == null && recessiveMap == null )
{
return null;
}
if ( dominantMap != null && recessiveMap == null )
{
return dominantMap;
}
if ( dominantMap == null && recessiveMap != null )
{
return recessiveMap;
}
Map result = new HashMap();
// Grab the keys from the dominant and recessive maps.
Set dominantMapKeys = dominantMap.keySet();
Set recessiveMapKeys = recessiveMap.keySet();
// Create the set of keys that will be contributed by the
// recessive Map by subtracting the intersection of keys
// from the recessive Map's keys.
Collection contributingRecessiveKeys =
CollectionUtils.subtract( recessiveMapKeys,
CollectionUtils.intersection( dominantMapKeys, recessiveMapKeys ) );
result.putAll( dominantMap );
// Now take the keys we just found and extract the values from
// the recessiveMap and put the key:value pairs into the dominantMap.
for ( Iterator i = contributingRecessiveKeys.iterator(); i.hasNext(); )
{
Object key = i.next();
result.put( key, recessiveMap.get( key ) );
}
return result;
}
/**
* Take a series of Map
s and merge
* them where the ordering of the array from 0..n
* is the dominant order.
*
* @param maps An array of Maps to merge.
* @return Map The result Map produced after the merging process.
*/
public static Map mergeMaps( Map[] maps )
{
Map result = null;
if ( maps.length == 0 )
{
result = null;
}
else if ( maps.length == 1 )
{
result = maps[0];
}
else
{
result = mergeMaps( maps[0], maps[1] );
for ( int i = 2; i < maps.length; i++ )
{
result = mergeMaps( result, maps[i] );
}
}
return result;
}
/**
* Returns a {@link Collection} containing the intersection
* of the given {@link Collection}s.
*
* The cardinality of each element in the returned {@link Collection}
* will be equal to the minimum of the cardinality of that element
* in the two given {@link Collection}s.
*
* @see Collection#retainAll
*/
public static Collection intersection( final Collection a, final Collection b )
{
ArrayList list = new ArrayList();
Map mapa = getCardinalityMap( a );
Map mapb = getCardinalityMap( b );
Set elts = new HashSet( a );
elts.addAll( b );
Iterator it = elts.iterator();
while ( it.hasNext() )
{
Object obj = it.next();
for ( int i = 0,m = Math.min( getFreq( obj, mapa ), getFreq( obj, mapb ) ); i < m; i++ )
{
list.add( obj );
}
}
return list;
}
/**
* Returns a {@link Collection} containing a - b.
* The cardinality of each element e in the returned {@link Collection}
* will be the cardinality of e in a minus the cardinality
* of e in b, or zero, whichever is greater.
*
* @see Collection#removeAll
*/
public static Collection subtract( final Collection a, final Collection b )
{
ArrayList list = new ArrayList( a );
Iterator it = b.iterator();
while ( it.hasNext() )
{
list.remove( it.next() );
}
return list;
}
/**
* Returns a {@link Map} mapping each unique element in
* the given {@link Collection} to an {@link Integer}
* representing the number of occurances of that element
* in the {@link Collection}.
* An entry that maps to null indicates that the
* element does not appear in the given {@link Collection}.
*/
public static Map getCardinalityMap( final Collection col )
{
HashMap count = new HashMap();
Iterator it = col.iterator();
while ( it.hasNext() )
{
Object obj = it.next();
Integer c = (Integer) ( count.get( obj ) );
if ( null == c )
{
count.put( obj, new Integer( 1 ) );
}
else
{
count.put( obj, new Integer( c.intValue() + 1 ) );
}
}
return count;
}
public static List iteratorToList( Iterator it )
{
if ( it == null )
{
throw new NullPointerException( "it cannot be null." );
}
List list = new ArrayList();
while ( it.hasNext() )
{
list.add( it.next() );
}
return list;
}
// ----------------------------------------------------------------------
//
// ----------------------------------------------------------------------
private static final int getFreq( final Object obj, final Map freqMap )
{
try
{
Object o = freqMap.get( obj );
if ( o != null ) // minimize NullPointerExceptions
{
return ( (Integer) o ).intValue();
}
}
catch ( NullPointerException e )
{
// ignored
}
catch ( NoSuchElementException e )
{
// ignored
}
return 0;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/DirectoryScanner.java 0000664 0000000 0000000 00000062066 11660305702 0027724 0 ustar 00root root 0000000 0000000 /*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.codehaus.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "Ant" and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact codehaus@codehaus.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*
* These criteria consist of selectors and patterns which have been specified. * With the selectors you can select which files you want to have included. * Files which are not selected are excluded. With patterns you can include * or exclude files based on their filename. *
* The idea is simple. A given directory is recursively scanned for all files * and directories. Each file/directory is matched against a set of selectors, * including special support for matching against filenames with include and * and exclude patterns. Only files/directories which match at least one * pattern of the include pattern list or other file selector, and don't match * any pattern of the exclude pattern list or fail to match against a required * selector will be placed in the list of files/directories found. *
* When no list of include patterns is supplied, "**" will be used, which * means that everything will be matched. When no list of exclude patterns is * supplied, an empty list is used, such that nothing will be excluded. When * no selectors are supplied, none are applied. *
* The filename pattern matching is done as follows:
* The name to be matched is split up in path segments. A path segment is the
* name of a directory or file, which is bounded by
* File.separator
('/' under UNIX, '\' under Windows).
* For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
* "def","ghi" and "xyz.java".
* The same is done for the pattern against which should be matched.
*
* The segments of the name and the pattern are then matched against each * other. When '**' is used for a path segment in the pattern, it matches * zero or more path segments of the name. *
* There is a special case regarding the use of File.separator
s
* at the beginning of the pattern and the string to match:
* When a pattern starts with a File.separator
, the string
* to match must also start with a File.separator
.
* When a pattern does not start with a File.separator
, the
* string to match may not start with a File.separator
.
* When one of these rules is not obeyed, the string will not
* match.
*
* When a name path segment is matched against a pattern path segment, the
* following special characters can be used:
* '*' matches zero or more characters
* '?' matches one character.
*
* Examples: *
* "**\*.class" matches all .class files/dirs in a directory tree. *
* "test\a??.java" matches all files/dirs which start with an 'a', then two * more characters and then ".java", in a directory called test. *
* "**" matches everything in a directory tree. *
* "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123"). *
* Case sensitivity may be turned off if necessary. By default, it is * turned on. *
* Example of usage: *
* String[] includes = {"**\\*.class"}; * String[] excludes = {"modules\\*\\**"}; * ds.setIncludes(includes); * ds.setExcludes(excludes); * ds.setBasedir(new File("test")); * ds.setCaseSensitive(true); * ds.scan(); * * System.out.println("FILES:"); * String[] files = ds.getIncludedFiles(); * for (int i = 0; i < files.length; i++) { * System.out.println(files[i]); * } ** This will scan a directory called test for .class files, but excludes all * files in all proper subdirectories of a directory called "modules" * * @author Arnout J. Kuiper * ajkuiper@wxs.nl * @author Magesh Umasankar * @author Bruce Atherton * @author Antoine Levy-Lambert */ public class DirectoryScanner extends AbstractScanner { /** The base directory to be scanned. */ protected File basedir; /** The files which matched at least one include and no excludes * and were selected. */ protected Vector filesIncluded; /** The files which did not match any includes or selectors. */ protected Vector filesNotIncluded; /** * The files which matched at least one include and at least * one exclude. */ protected Vector filesExcluded; /** The directories which matched at least one include and no excludes * and were selected. */ protected Vector dirsIncluded; /** The directories which were found and did not match any includes. */ protected Vector dirsNotIncluded; /** * The directories which matched at least one include and at least one * exclude. */ protected Vector dirsExcluded; /** The files which matched at least one include and no excludes and * which a selector discarded. */ protected Vector filesDeselected; /** The directories which matched at least one include and no excludes * but which a selector discarded. */ protected Vector dirsDeselected; /** Whether or not our results were built by a slow scan. */ protected boolean haveSlowResults = false; /** * Whether or not symbolic links should be followed. * * @since Ant 1.5 */ private boolean followSymlinks = true; /** Whether or not everything tested so far has been included. */ protected boolean everythingIncluded = true; /** * Sole constructor. */ public DirectoryScanner() { } /** * Sets the base directory to be scanned. This is the directory which is * scanned recursively. All '/' and '\' characters are replaced by *
File.separatorChar
, so the separator used need not match
* File.separatorChar
.
*
* @param basedir The base directory to scan.
* Must not be null
.
*/
public void setBasedir( String basedir )
{
setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace(
'\\', File.separatorChar ) ) );
}
/**
* Sets the base directory to be scanned. This is the directory which is
* scanned recursively.
*
* @param basedir The base directory for scanning.
* Should not be null
.
*/
public void setBasedir( File basedir )
{
this.basedir = basedir;
}
/**
* Returns the base directory to be scanned.
* This is the directory which is scanned recursively.
*
* @return the base directory to be scanned
*/
public File getBasedir()
{
return basedir;
}
/**
* Sets whether or not symbolic links should be followed.
*
* @param followSymlinks whether or not symbolic links should be followed
*/
public void setFollowSymlinks( boolean followSymlinks )
{
this.followSymlinks = followSymlinks;
}
/**
* Returns whether or not the scanner has included all the files or
* directories it has come across so far.
*
* @return true
if all files and directories which have
* been found so far have been included.
*/
public boolean isEverythingIncluded()
{
return everythingIncluded;
}
/**
* Scans the base directory for files which match at least one include
* pattern and don't match any exclude patterns. If there are selectors
* then the files must pass muster there, as well.
*
* @exception IllegalStateException if the base directory was set
* incorrectly (i.e. if it is null
, doesn't exist,
* or isn't a directory).
*/
public void scan() throws IllegalStateException
{
if ( basedir == null )
{
throw new IllegalStateException( "No basedir set" );
}
if ( !basedir.exists() )
{
throw new IllegalStateException( "basedir " + basedir
+ " does not exist" );
}
if ( !basedir.isDirectory() )
{
throw new IllegalStateException( "basedir " + basedir
+ " is not a directory" );
}
setupDefaultFilters();
filesIncluded = new Vector();
filesNotIncluded = new Vector();
filesExcluded = new Vector();
filesDeselected = new Vector();
dirsIncluded = new Vector();
dirsNotIncluded = new Vector();
dirsExcluded = new Vector();
dirsDeselected = new Vector();
if ( isIncluded( "" ) )
{
if ( !isExcluded( "" ) )
{
if ( isSelected( "", basedir ) )
{
dirsIncluded.addElement( "" );
}
else
{
dirsDeselected.addElement( "" );
}
}
else
{
dirsExcluded.addElement( "" );
}
}
else
{
dirsNotIncluded.addElement( "" );
}
scandir( basedir, "", true );
}
/**
* Top level invocation for a slow scan. A slow scan builds up a full
* list of excluded/included files/directories, whereas a fast scan
* will only have full results for included files, as it ignores
* directories which can't possibly hold any included files/directories.
*
* Returns immediately if a slow scan has already been completed.
*/
protected void slowScan()
{
if ( haveSlowResults )
{
return;
}
String[] excl = new String[dirsExcluded.size()];
dirsExcluded.copyInto( excl );
String[] notIncl = new String[dirsNotIncluded.size()];
dirsNotIncluded.copyInto( notIncl );
for ( int i = 0; i < excl.length; i++ )
{
if ( !couldHoldIncluded( excl[i] ) )
{
scandir( new File( basedir, excl[i] ),
excl[i] + File.separator, false );
}
}
for ( int i = 0; i < notIncl.length; i++ )
{
if ( !couldHoldIncluded( notIncl[i] ) )
{
scandir( new File( basedir, notIncl[i] ),
notIncl[i] + File.separator, false );
}
}
haveSlowResults = true;
}
/**
* Scans the given directory for files and directories. Found files and
* directories are placed in their respective collections, based on the
* matching of includes, excludes, and the selectors. When a directory
* is found, it is scanned recursively.
*
* @param dir The directory to scan. Must not be null
.
* @param vpath The path relative to the base directory (needed to
* prevent problems with an absolute path when using
* dir). Must not be null
.
* @param fast Whether or not this call is part of a fast scan.
* @throws IOException
*
* @see #filesIncluded
* @see #filesNotIncluded
* @see #filesExcluded
* @see #dirsIncluded
* @see #dirsNotIncluded
* @see #dirsExcluded
* @see #slowScan
*/
protected void scandir( File dir, String vpath, boolean fast )
{
String[] newfiles = dir.list();
if ( newfiles == null )
{
/*
* two reasons are mentioned in the API docs for File.list
* (1) dir is not a directory. This is impossible as
* we wouldn't get here in this case.
* (2) an IO error occurred (why doesn't it throw an exception
* then???)
*/
/*
* [jdcasey] (2) is apparently happening to me, as this is killing one of my tests...
* this is affecting the assembly plugin, fwiw. I will initialize the newfiles array as
* zero-length for now.
*
* NOTE: I can't find the problematic code, as it appears to come from a native method
* in UnixFileSystem...
*/
/*
* [bentmann] A null array will also be returned from list() on NTFS when dir refers to a soft link or
* junction point whose target is not existent.
*/
newfiles = new String[0];
// throw new IOException( "IO error scanning directory " + dir.getAbsolutePath() );
}
if ( !followSymlinks )
{
Vector noLinks = new Vector();
for ( int i = 0; i < newfiles.length; i++ )
{
try
{
if ( isSymbolicLink( dir, newfiles[i] ) )
{
String name = vpath + newfiles[i];
File file = new File( dir, newfiles[i] );
if ( file.isDirectory() )
{
dirsExcluded.addElement( name );
}
else
{
filesExcluded.addElement( name );
}
}
else
{
noLinks.addElement( newfiles[i] );
}
}
catch ( IOException ioe )
{
String msg = "IOException caught while checking "
+ "for links, couldn't get cannonical path!";
// will be caught and redirected to Ant's logging system
System.err.println( msg );
noLinks.addElement( newfiles[i] );
}
}
newfiles = new String[noLinks.size()];
noLinks.copyInto( newfiles );
}
for ( int i = 0; i < newfiles.length; i++ )
{
String name = vpath + newfiles[i];
File file = new File( dir, newfiles[i] );
if ( file.isDirectory() )
{
if ( isIncluded( name ) )
{
if ( !isExcluded( name ) )
{
if ( isSelected( name, file ) )
{
dirsIncluded.addElement( name );
if ( fast )
{
scandir( file, name + File.separator, fast );
}
}
else
{
everythingIncluded = false;
dirsDeselected.addElement( name );
if ( fast && couldHoldIncluded( name ) )
{
scandir( file, name + File.separator, fast );
}
}
}
else
{
everythingIncluded = false;
dirsExcluded.addElement( name );
if ( fast && couldHoldIncluded( name ) )
{
scandir( file, name + File.separator, fast );
}
}
}
else
{
everythingIncluded = false;
dirsNotIncluded.addElement( name );
if ( fast && couldHoldIncluded( name ) )
{
scandir( file, name + File.separator, fast );
}
}
if ( !fast )
{
scandir( file, name + File.separator, fast );
}
}
else if ( file.isFile() )
{
if ( isIncluded( name ) )
{
if ( !isExcluded( name ) )
{
if ( isSelected( name, file ) )
{
filesIncluded.addElement( name );
}
else
{
everythingIncluded = false;
filesDeselected.addElement( name );
}
}
else
{
everythingIncluded = false;
filesExcluded.addElement( name );
}
}
else
{
everythingIncluded = false;
filesNotIncluded.addElement( name );
}
}
}
}
/**
* Tests whether a name should be selected.
*
* @param name the filename to check for selecting
* @param file the java.io.File object for this filename
* @return false
when the selectors says that the file
* should not be selected, true
otherwise.
*/
protected boolean isSelected( String name, File file )
{
return true;
}
/**
* Returns the names of the files which matched at least one of the
* include patterns and none of the exclude patterns.
* The names are relative to the base directory.
*
* @return the names of the files which matched at least one of the
* include patterns and none of the exclude patterns.
*/
public String[] getIncludedFiles()
{
String[] files = new String[filesIncluded.size()];
filesIncluded.copyInto( files );
return files;
}
/**
* Returns the names of the files which matched none of the include
* patterns. The names are relative to the base directory. This involves
* performing a slow scan if one has not already been completed.
*
* @return the names of the files which matched none of the include
* patterns.
*
* @see #slowScan
*/
public String[] getNotIncludedFiles()
{
slowScan();
String[] files = new String[filesNotIncluded.size()];
filesNotIncluded.copyInto( files );
return files;
}
/**
* Returns the names of the files which matched at least one of the
* include patterns and at least one of the exclude patterns.
* The names are relative to the base directory. This involves
* performing a slow scan if one has not already been completed.
*
* @return the names of the files which matched at least one of the
* include patterns and at at least one of the exclude patterns.
*
* @see #slowScan
*/
public String[] getExcludedFiles()
{
slowScan();
String[] files = new String[filesExcluded.size()];
filesExcluded.copyInto( files );
return files;
}
/**
*
Returns the names of the files which were selected out and * therefore not ultimately included.
* *The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed.
* * @return the names of the files which were deselected. * * @see #slowScan */ public String[] getDeselectedFiles() { slowScan(); String[] files = new String[filesDeselected.size()]; filesDeselected.copyInto( files ); return files; } /** * Returns the names of the directories which matched at least one of the * include patterns and none of the exclude patterns. * The names are relative to the base directory. * * @return the names of the directories which matched at least one of the * include patterns and none of the exclude patterns. */ public String[] getIncludedDirectories() { String[] directories = new String[dirsIncluded.size()]; dirsIncluded.copyInto( directories ); return directories; } /** * Returns the names of the directories which matched none of the include * patterns. The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed. * * @return the names of the directories which matched none of the include * patterns. * * @see #slowScan */ public String[] getNotIncludedDirectories() { slowScan(); String[] directories = new String[dirsNotIncluded.size()]; dirsNotIncluded.copyInto( directories ); return directories; } /** * Returns the names of the directories which matched at least one of the * include patterns and at least one of the exclude patterns. * The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed. * * @return the names of the directories which matched at least one of the * include patterns and at least one of the exclude patterns. * * @see #slowScan */ public String[] getExcludedDirectories() { slowScan(); String[] directories = new String[dirsExcluded.size()]; dirsExcluded.copyInto( directories ); return directories; } /** *Returns the names of the directories which were selected out and * therefore not ultimately included.
* *The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed.
* * @return the names of the directories which were deselected. * * @see #slowScan */ public String[] getDeselectedDirectories() { slowScan(); String[] directories = new String[dirsDeselected.size()]; dirsDeselected.copyInto( directories ); return directories; } /** * Checks whether a given file is a symbolic link. * *It doesn't really test for symbolic links but whether the * canonical and absolute paths of the file are identical - this * may lead to false positives on some platforms.
* * @param parent the parent directory of the file to test * @param name the name of the file to test. * * @since Ant 1.5 */ public boolean isSymbolicLink( File parent, String name ) throws IOException { File resolvedParent = new File( parent.getCanonicalPath() ); File toTest = new File( resolvedParent, name ); return !toTest.getAbsolutePath().equals( toTest.getCanonicalPath() ); } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/DirectoryWalkListener.java 0000664 0000000 0000000 00000002530 11660305702 0030725 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util; /* * Copyright The Codehaus Foundation. * * 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. */ import java.io.File; /** * DirectoryWalkListener * @version $Id: DirectoryWalkListener.java 8010 2009-01-07 12:59:50Z vsiveton $ */ public interface DirectoryWalkListener { /** * The directory walking has begun. * * @param basedir the basedir that walk started in. */ void directoryWalkStarting( File basedir ); /** * The included entry that was encountered. * * @param percentage rough percentage of the walk completed. (inaccurate) * @param file the file that was included. */ void directoryWalkStep( int percentage, File file ); /** * The directory walking has finished. */ void directoryWalkFinished(); void debug( String message ); } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/DirectoryWalker.java 0000664 0000000 0000000 00000026571 11660305702 0027561 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util; /* * Copyright The Codehaus Foundation. * * 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. */ import java.io.File; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Stack; /** * DirectoryWalker * @version $Id: DirectoryWalker.java 8010 2009-01-07 12:59:50Z vsiveton $ */ public class DirectoryWalker { /** * DirStackEntry is an Item on the {@link DirectoryWalker#dirStack} */ class DirStackEntry { /** * Count of files in the directory. */ public int count; /** * Current Directory. */ public File dir; /** * Index (or offset) within the directory count. */ public int index; /** * Offset for percentage calculations. Based on parent DirStackEntry. */ public double percentageOffset; /** * Size of percentage space to work with. */ public double percentageSize; /** * Create a DirStackEntry. * * @param d the directory to track * @param length the length of entries in the directory. */ public DirStackEntry( File d, int length ) { dir = d; count = length; } /** * Calculate the next percentage offset. * Used by the next DirStackEntry. * * @return the value for the next percentage offset. */ public double getNextPercentageOffset() { return percentageOffset + ( index * ( percentageSize / count ) ); } /** * Calculate the next percentage size. * Used by the next DirStackEntry. * * @return the value for the next percentage size. */ public double getNextPercentageSize() { return ( percentageSize / count ); } /** * The percentage of the DirStackEntry right now. * Based on count, index, percentageOffset, and percentageSize. * * @return the percentage right now. */ public int getPercentage() { double percentageWithinDir = (double) index / (double) count; return (int) Math.floor( percentageOffset + ( percentageWithinDir * percentageSize ) ); } public String toString() { return "DirStackEntry[" + "dir=" + dir.getAbsolutePath() + ",count=" + count + ",index=" + index + ",percentageOffset=" + percentageOffset + ",percentageSize=" + percentageSize + ",percentage()=" + getPercentage() + ",getNextPercentageOffset()=" + getNextPercentageOffset() + ",getNextPercentageSize()=" + getNextPercentageSize() + "]"; } } private File baseDir; private int baseDirOffset; private Stack dirStack; private List excludes; private List includes; private boolean isCaseSensitive = true; private List listeners; private boolean debugEnabled = false; public DirectoryWalker() { this.includes = new ArrayList(); this.excludes = new ArrayList(); this.listeners = new ArrayList(); } public void addDirectoryWalkListener( DirectoryWalkListener listener ) { this.listeners.add( listener ); } public void addExclude( String exclude ) { this.excludes.add( fixPattern( exclude ) ); } public void addInclude( String include ) { this.includes.add( fixPattern( include ) ); } /** * Add's to the Exclude List the default list of SCM excludes. */ public void addSCMExcludes() { String scmexcludes[] = DirectoryScanner.DEFAULTEXCLUDES; for ( int i = 0; i < scmexcludes.length; i++ ) { addExclude( scmexcludes[i] ); } } private void fireStep( File file ) { DirStackEntry dsEntry = (DirStackEntry) dirStack.peek(); int percentage = dsEntry.getPercentage(); Iterator it = this.listeners.iterator(); while ( it.hasNext() ) { DirectoryWalkListener listener = (DirectoryWalkListener) it.next(); listener.directoryWalkStep( percentage, file ); } } private void fireWalkFinished() { Iterator it = this.listeners.iterator(); while ( it.hasNext() ) { DirectoryWalkListener listener = (DirectoryWalkListener) it.next(); listener.directoryWalkFinished(); } } private void fireWalkStarting() { Iterator it = this.listeners.iterator(); while ( it.hasNext() ) { DirectoryWalkListener listener = (DirectoryWalkListener) it.next(); listener.directoryWalkStarting( this.baseDir ); } } private void fireDebugMessage( String message ) { Iterator it = this.listeners.iterator(); while ( it.hasNext() ) { DirectoryWalkListener listener = (DirectoryWalkListener) it.next(); listener.debug( message ); } } private String fixPattern( String pattern ) { String cleanPattern = pattern; if ( File.separatorChar != '/' ) { cleanPattern = cleanPattern.replace( '/', File.separatorChar ); } if ( File.separatorChar != '\\' ) { cleanPattern = cleanPattern.replace( '\\', File.separatorChar ); } return cleanPattern; } public void setDebugMode( boolean debugEnabled ) { this.debugEnabled = debugEnabled; } /** * @return Returns the baseDir. */ public File getBaseDir() { return baseDir; } /** * @return Returns the excludes. */ public List getExcludes() { return excludes; } /** * @return Returns the includes. */ public List getIncludes() { return includes; } private boolean isExcluded( String name ) { return isMatch( this.excludes, name ); } private boolean isIncluded( String name ) { return isMatch( this.includes, name ); } private boolean isMatch( List patterns, String name ) { Iterator it = patterns.iterator(); while ( it.hasNext() ) { String pattern = (String) it.next(); if ( SelectorUtils.matchPath( pattern, name, isCaseSensitive ) ) { return true; } } return false; } private String relativeToBaseDir( File file ) { return file.getAbsolutePath().substring( baseDirOffset + 1 ); } /** * Removes a DirectoryWalkListener. * * @param listener the listener to remove. */ public void removeDirectoryWalkListener( DirectoryWalkListener listener ) { this.listeners.remove( listener ); } /** * Performs a Scan against the provided {@link #setBaseDir(File)} */ public void scan() { if ( baseDir == null ) { throw new IllegalStateException( "Scan Failure. BaseDir not specified." ); } if ( !baseDir.exists() ) { throw new IllegalStateException( "Scan Failure. BaseDir does not exist." ); } if ( !baseDir.isDirectory() ) { throw new IllegalStateException( "Scan Failure. BaseDir is not a directory." ); } if ( this.includes.isEmpty() ) { // default to include all. addInclude( "**" ); } if ( debugEnabled ) { Iterator it; StringBuffer dbg = new StringBuffer(); dbg.append( "DirectoryWalker Scan" ); dbg.append( "\n Base Dir: " ).append( this.baseDir.getAbsolutePath() ); dbg.append( "\n Includes: " ); it = this.includes.iterator(); while ( it.hasNext() ) { String include = (String) it.next(); dbg.append( "\n - \"" ).append( include ).append( "\"" ); } dbg.append( "\n Excludes: " ); it = this.excludes.iterator(); while ( it.hasNext() ) { String exclude = (String) it.next(); dbg.append( "\n - \"" ).append( exclude ).append( "\"" ); } fireDebugMessage( dbg.toString() ); } fireWalkStarting(); dirStack = new Stack(); scanDir( this.baseDir ); fireWalkFinished(); } private void scanDir( File dir ) { File files[] = dir.listFiles(); if ( files == null ) { return; } DirStackEntry curStackEntry = new DirStackEntry( dir, files.length ); if ( dirStack.isEmpty() ) { curStackEntry.percentageOffset = 0; curStackEntry.percentageSize = 100; } else { DirStackEntry previousStackEntry = (DirStackEntry) dirStack.peek(); curStackEntry.percentageOffset = previousStackEntry.getNextPercentageOffset(); curStackEntry.percentageSize = previousStackEntry.getNextPercentageSize(); } dirStack.push( curStackEntry ); for ( int idx = 0; idx < files.length; idx++ ) { curStackEntry.index = idx; String name = relativeToBaseDir( files[idx] ); if ( isExcluded( name ) ) { fireDebugMessage( name + " is excluded." ); continue; } if ( files[idx].isDirectory() ) { scanDir( files[idx] ); } else { if ( isIncluded( name ) ) { fireStep( files[idx] ); } } } dirStack.pop(); } /** * @param baseDir The baseDir to set. */ public void setBaseDir( File baseDir ) { this.baseDir = baseDir; this.baseDirOffset = baseDir.getAbsolutePath().length(); } /** * @param entries The excludes to set. */ public void setExcludes( List entries ) { this.excludes.clear(); if ( entries != null ) { Iterator it = entries.iterator(); while ( it.hasNext() ) { String pattern = (String) it.next(); this.excludes.add( fixPattern( pattern ) ); } } } /** * @param entries The includes to set. */ public void setIncludes( List entries ) { this.includes.clear(); if ( entries != null ) { Iterator it = entries.iterator(); while ( it.hasNext() ) { String pattern = (String) it.next(); this.includes.add( fixPattern( pattern ) ); } } } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/ExceptionUtils.java 0000664 0000000 0000000 00000052677 11660305702 0027434 0 ustar 00root root 0000000 0000000 /* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2002-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.codehaus.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact codehaus@codehaus.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR * ITS 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. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see *ExceptionUtils
provides utilities for manipulating
* Throwable
objects.
ExceptionUtils
. Protected to
* discourage instantiation.
*/
protected ExceptionUtils()
{
}
/**
* Adds to the list of method names used in the search for Throwable
* objects.
Introspects the specified Throwable
to obtain the cause.
The method searches for methods with specific names that return a
* Throwable
object. This will pick up most wrapping exceptions,
* including those from JDK 1.4, and
* The method names can be added to using {@link #addCauseMethodName(String)}.
* The default list searched for are:
getCause()
* getNextException()
* getTargetException()
* getException()
* getSourceException()
* getRootCause()
* getCausedByException()
* getNested()
* In the absence of any such method, the object is inspected for a
* detail
field assignable to a Throwable
.
If none of the above is found, returns null
.
Throwable
.
* @throws NullPointerException if the throwable is null
*/
public static Throwable getCause( Throwable throwable )
{
return getCause( throwable, CAUSE_METHOD_NAMES );
}
/**
* Introspects the specified Throwable
to obtain the cause
* using a supplied array of method names.
Throwable
.
* @throws NullPointerException if the method names array is null or contains null
* @throws NullPointerException if the throwable is null
*/
public static Throwable getCause( Throwable throwable, String[] methodNames )
{
Throwable cause = getCauseUsingWellKnownTypes( throwable );
if ( cause == null )
{
for ( int i = 0; i < methodNames.length; i++ )
{
cause = getCauseUsingMethodName( throwable, methodNames[i] );
if ( cause != null )
{
break;
}
}
if ( cause == null )
{
cause = getCauseUsingFieldName( throwable, "detail" );
}
}
return cause;
}
/**
* Walks through the exception chain to the last element -- the * "root" of the tree -- using {@link #getCause(Throwable)}, and * returns that exception.
* * @param throwable the throwable to get the root cause for * @return The root cause of theThrowable
.
*/
public static Throwable getRootCause( Throwable throwable )
{
Throwable cause = getCause( throwable );
if ( cause != null )
{
throwable = cause;
while ( ( throwable = getCause( throwable ) ) != null )
{
cause = throwable;
}
}
return cause;
}
/**
* Uses instanceof
checks to examine the exception,
* looking for well known types which could contain chained or
* wrapped exceptions.
null
if not
* found.
*/
private static Throwable getCauseUsingWellKnownTypes( Throwable throwable )
{
if ( throwable instanceof SQLException )
{
return ( (SQLException) throwable ).getNextException();
}
else if ( throwable instanceof InvocationTargetException )
{
return ( (InvocationTargetException) throwable ).getTargetException();
}
else
{
return null;
}
}
/**
* Find a throwable by method name.
* * @param throwable the exception to examine * @param methodName the name of the method to find and invoke * @return The wrapped exception, ornull
if not
* found.
*/
private static Throwable getCauseUsingMethodName( Throwable throwable, String methodName )
{
Method method = null;
try
{
method = throwable.getClass().getMethod( methodName, null );
}
catch ( NoSuchMethodException ignored )
{
}
catch ( SecurityException ignored )
{
}
if ( method != null && Throwable.class.isAssignableFrom( method.getReturnType() ) )
{
try
{
return (Throwable) method.invoke( throwable, new Object[0] );
}
catch ( IllegalAccessException ignored )
{
}
catch ( IllegalArgumentException ignored )
{
}
catch ( InvocationTargetException ignored )
{
}
}
return null;
}
/**
* Find a throwable by field name.
* * @param throwable the exception to examine * @param fieldName the name of the attribute to examine * @return The wrapped exception, ornull
if not
* found.
*/
private static Throwable getCauseUsingFieldName( Throwable throwable, String fieldName )
{
Field field = null;
try
{
field = throwable.getClass().getField( fieldName );
}
catch ( NoSuchFieldException ignored )
{
}
catch ( SecurityException ignored )
{
}
if ( field != null && Throwable.class.isAssignableFrom( field.getType() ) )
{
try
{
return (Throwable) field.get( throwable );
}
catch ( IllegalAccessException ignored )
{
}
catch ( IllegalArgumentException ignored )
{
}
}
return null;
}
/**
* Returns the number of Throwable
objects in the
* exception chain.
Returns the list of Throwable
objects in the
* exception chain.
Throwable
objects.
*/
public static Throwable[] getThrowables( Throwable throwable )
{
List list = new ArrayList();
while ( throwable != null )
{
list.add( throwable );
throwable = ExceptionUtils.getCause( throwable );
}
return (Throwable[]) list.toArray( new Throwable[list.size()] );
}
/**
* Delegates to {@link #indexOfThrowable(Throwable, Class, int)}, * starting the search at the beginning of the exception chain.
* * @see #indexOfThrowable(Throwable, Class, int) */ public static int indexOfThrowable( Throwable throwable, Class type ) { return indexOfThrowable( throwable, type, 0 ); } /** *Returns the (zero based) index, of the first
* Throwable
that matches the specified type in the
* exception chain of Throwable
objects with an index
* greater than or equal to the specified index, or
* -1
if the type is not found.
Class
to look for
* @param fromIndex the (zero based) index of the starting
* position in the chain to be searched
* @return the first occurrence of the type in the chain, or
* -1
if the type is not found
* @throws IndexOutOfBoundsException If the fromIndex
* argument is negative or not less than the count of
* Throwable
s in the chain.
*/
public static int indexOfThrowable( Throwable throwable, Class type, int fromIndex )
{
if ( fromIndex < 0 )
{
throw new IndexOutOfBoundsException( "Throwable index out of range: " + fromIndex );
}
Throwable[] throwables = ExceptionUtils.getThrowables( throwable );
if ( fromIndex >= throwables.length )
{
throw new IndexOutOfBoundsException( "Throwable index out of range: " + fromIndex );
}
for ( int i = fromIndex; i < throwables.length; i++ )
{
if ( throwables[i].getClass().equals( type ) )
{
return i;
}
}
return -1;
}
/**
* Prints a compact stack trace for the root cause of a throwable.
* 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.
*
* The method is equivalent to t.printStackTrace() for throwables
* that don't have nested causes.
*/
public static void printRootCauseStackTrace( Throwable t, PrintStream stream )
{
String trace[] = getRootCauseStackTrace( t );
for ( int i = 0; i < trace.length; i++ )
{
stream.println( trace[i] );
}
stream.flush();
}
/**
* Equivalent to printRootCauseStackTrace(t, System.err)
*/
public static void printRootCauseStackTrace( Throwable t )
{
printRootCauseStackTrace( t, System.err );
}
/**
* Same as printRootCauseStackTrace(t, stream), except it takes
* a PrintWriter as an argument.
*/
public static void printRootCauseStackTrace( Throwable t, PrintWriter writer )
{
String trace[] = getRootCauseStackTrace( t );
for ( int i = 0; i < trace.length; i++ )
{
writer.println( trace[i] );
}
writer.flush();
}
/**
* Creates a compact stack trace for the root cause of the supplied
* throwable.
*
* See This class represents a Our benchmark
* indicates that {@link FastMap#put FastMap.put(key, value)} is up to
* 5x faster than {@link FastMap} has a predictable iteration order, which is the order
* in which keys were inserted into the map (similar to
* Applications may change the resizing policy of {@link FastMap}
* by overriding the {@link #sizeChanged} method. For example, to improve
* predictability, automatic resizing can be disabled. This implementation is not synchronized. Multiple threads accessing
* or modifying the collection must be synchronized externally. Note: To avoid dynamic memory allocations, {@link FastMap}
* maintains an internal pool of This class is public domain (not copyrighted). Methods exist to retrieve the components of a typical file path. For example
* printRootCauseStackTrace(Throwable t, PrintStream s)
*/
public static String[] getRootCauseStackTrace( Throwable t )
{
Throwable throwables[] = getThrowables( t );
int count = throwables.length;
ArrayList frames = new ArrayList();
List nextTrace = getStackFrameList( throwables[count - 1] );
for ( int i = count; --i >= 0; )
{
List 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 (String[]) frames.toArray( new String[0] );
}
/**
* Given two stack traces, removes common frames from the cause trace.
*
* @param causeFrames stack trace of a cause throwable
* @param wrapperFrames stack trace of a wrapper throwable
*/
private static void removeCommonFrames( List causeFrames, List wrapperFrames )
{
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
String causeFrame = (String) causeFrames.get( causeFrameIndex );
String wrapperFrame = (String) wrapperFrames.get( wrapperFrameIndex );
if ( causeFrame.equals( wrapperFrame ) )
{
causeFrames.remove( causeFrameIndex );
}
causeFrameIndex--;
wrapperFrameIndex--;
}
}
/**
* A convenient way of extracting the stack trace from an
* exception.
*
* @param t The Throwable
.
* @return The stack trace as generated by the exception's
* printStackTrace(PrintWriter)
method.
*/
public static String getStackTrace( Throwable t )
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter( sw, true );
t.printStackTrace( pw );
return sw.getBuffer().toString();
}
/**
* A way to get the entire nested stack-trace of an throwable.
*
* @param t The Throwable
.
* @return The nested stack trace, with the root cause first.
*/
public static String getFullStackTrace( Throwable t )
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter( sw, true );
Throwable[] ts = getThrowables( t );
for ( int i = 0; i < ts.length; i++ )
{
ts[i].printStackTrace( pw );
if ( isNestedThrowable( ts[i] ) )
{
break;
}
}
return sw.getBuffer().toString();
}
/**
* Whether an Throwable is considered nested or not.
*
* @param throwable The Throwable
.
* @return boolean true/false
*/
public static boolean isNestedThrowable( Throwable throwable )
{
if ( throwable == null )
{
return false;
}
if ( throwable instanceof SQLException )
{
return true;
}
else if ( throwable instanceof InvocationTargetException )
{
return true;
}
int sz = CAUSE_METHOD_NAMES.length;
for ( int i = 0; i < sz; i++ )
{
try
{
Method method = throwable.getClass().getMethod( CAUSE_METHOD_NAMES[i], null );
if ( method != null )
{
return true;
}
}
catch ( NoSuchMethodException ignored )
{
}
catch ( SecurityException ignored )
{
}
}
try
{
Field field = throwable.getClass().getField( "detail" );
if ( field != null )
{
return true;
}
}
catch ( NoSuchFieldException ignored )
{
}
catch ( SecurityException ignored )
{
}
return false;
}
/**
* Captures the stack trace associated with the specified
* Throwable
object, decomposing it into a list of
* stack frames.
*
* @param t The Throwable
.
* @return An array of strings describing each stack frame.
*/
public static String[] getStackFrames( Throwable t )
{
return getStackFrames( getStackTrace( t ) );
}
/**
* Functionality shared between the
* getStackFrames(Throwable)
methods of this and the
* classes.
*/
static String[] getStackFrames( String stackTrace )
{
String linebreak = System.getProperty( "line.separator" );
StringTokenizer frames = new StringTokenizer( stackTrace, linebreak );
List list = new LinkedList();
while ( frames.hasMoreTokens() )
{
list.add( frames.nextToken() );
}
return (String[]) list.toArray( new String[]{
} );
}
/**
* Produces a List of stack frames - the message is not included.
* This works in most cases - it will only fail if the exception message
* contains a line that starts with: " at".
*
* @param t is any throwable
* @return List of stack frames
*/
static List getStackFrameList( Throwable t )
{
String stackTrace = getStackTrace( t );
String linebreak = System.getProperty( "line.separator" );
StringTokenizer frames = new StringTokenizer( stackTrace, linebreak );
List list = new LinkedList();
boolean traceStarted = false;
while ( frames.hasMoreTokens() )
{
String token = frames.nextToken();
// Determine if the line starts with Map
collection with real-time
* behavior. Unless the map's size exceeds its current capacity,
* no dynamic memory allocation is ever performed and response time is
* extremely fast and consistent.java.util.HashMap.put(key, value)
.
* This difference is mostly due to the cost of the Map.Entry
* allocations that {@link FastMap} avoids by recycling its entries
* (see note below).java.util.LinkedHashMap
collection class).Map.Entry
objects. The size
* of the pool is determined by the map's capacity. When an entry is
* removed from the map, it is automatically restored to the pool.256
entries.
*/
public FastMap() {
initialize(256);
}
/**
* Creates a {@link FastMap}, copy of the specified Map
.
* If the specified map is not an instance of {@link FastMap}, the
* newly created map has a capacity set to the specified map's size.
* The copy has the same order as the original, regardless of the original
* map's implementation:
* TreeMap dictionary = ...;
* FastMap dictionaryLookup = new FastMap(dictionary);
*
*
* @param map the map whose mappings are to be placed in this map.
*/
public FastMap(Map map) {
int capacity = (map instanceof FastMap) ?
((FastMap)map).capacity() : map.size();
initialize(capacity);
putAll(map);
}
/**
* Creates a {@link FastMap} with the specified capacity. Unless the
* capacity is exceeded, operations on this map do not allocate entries.
* For optimum performance, the capacity should be of the same order
* of magnitude or larger than the expected map's size.
*
* @param capacity the number of buckets in the hash table; it also
* defines the number of pre-allocated entries.
*/
public FastMap(int capacity) {
initialize(capacity);
}
/**
* Returns the number of key-value mappings in this {@link FastMap}.
*
* @return this map's size.
*/
public int size() {
return _size;
}
/**
* Returns the capacity of this {@link FastMap}. The capacity defines
* the number of buckets in the hash table, as well as the maximum number
* of entries the map may contain without allocating memory.
*
* @return this map's capacity.
*/
public int capacity() {
return _capacity;
}
/**
* Indicates if this {@link FastMap} contains no key-value mappings.
*
* @return true
if this map contains no key-value mappings;
* false
otherwise.
*/
public boolean isEmpty() {
return _size == 0;
}
/**
* Indicates if this {@link FastMap} contains a mapping for the specified
* key.
*
* @param key the key whose presence in this map is to be tested.
* @return true
if this map contains a mapping for the
* specified key; false
otherwise.
* @throws NullPointerException if the key is null
.
*/
public boolean containsKey(Object key) {
EntryImpl entry = _entries[keyHash(key) & _mask];
while (entry != null) {
if (key.equals(entry._key) ) {
return true;
}
entry = entry._next;
}
return false;
}
/**
* Indicates if this {@link FastMap} maps one or more keys to the
* specified value.
*
* @param value the value whose presence in this map is to be tested.
* @return true
if this map maps one or more keys to the
* specified value.
* @throws NullPointerException if the key is null
.
*/
public boolean containsValue(Object value) {
EntryImpl entry = _mapFirst;
while (entry != null) {
if (value.equals(entry._value) ) {
return true;
}
entry = entry._after;
}
return false;
}
/**
* Returns the value to which this {@link FastMap} maps the specified key.
*
* @param key the key whose associated value is to be returned.
* @return the value to which this map maps the specified key,
* or null
if there is no mapping for the key.
* @throws NullPointerException if key is null
.
*/
public Object get(Object key) {
EntryImpl entry = _entries[keyHash(key) & _mask];
while (entry != null) {
if (key.equals(entry._key) ) {
return entry._value;
}
entry = entry._next;
}
return null;
}
/**
* Returns the entry with the specified key.
*
* @param key the key whose associated entry is to be returned.
* @return the entry for the specified key or null
if none.
*/
public Map.Entry getEntry(Object key) {
EntryImpl entry = _entries[keyHash(key) & _mask];
while (entry != null) {
if (key.equals(entry._key)) {
return entry;
}
entry = entry._next;
}
return null;
}
/**
* Associates the specified value with the specified key in this
* {@link FastMap}. If the {@link FastMap} previously contained a mapping
* for this key, the old value is replaced.
*
* @param key the key with which the specified value is to be associated.
* @param value the value to be associated with the specified key.
* @return the previous value associated with specified key,
* or null
if there was no mapping for key.
* A null
return can also indicate that the map
* previously associated null
with the specified key.
* @throws NullPointerException if the key is null
.
*/
public Object put(Object key, Object value) {
EntryImpl entry = _entries[keyHash(key) & _mask];
while (entry != null) {
if (key.equals(entry._key) ) {
Object prevValue = entry._value;
entry._value = value;
return prevValue;
}
entry = entry._next;
}
// No previous mapping.
addEntry(key, value);
return null;
}
/**
* Copies all of the mappings from the specified map to this
* {@link FastMap}.
*
* @param map the mappings to be stored in this map.
* @throws NullPointerException the specified map is null
, or
* the specified map contains null
keys.
*/
public void putAll(Map map) {
for (Iterator i = map.entrySet().iterator(); i.hasNext(); ) {
Map.Entry e = (Map.Entry) i.next();
addEntry(e.getKey(), e.getValue());
}
}
/**
* Removes the mapping for this key from this {@link FastMap} if present.
*
* @param key the key whose mapping is to be removed from the map.
* @return previous value associated with specified key,
* or null
if there was no mapping for key.
* A null
return can also indicate that the map
* previously associated null
with the specified key.
* @throws NullPointerException if the key is null
.
*/
public Object remove(Object key) {
EntryImpl entry = _entries[keyHash(key) & _mask];
while (entry != null) {
if (key.equals(entry._key) ) {
Object prevValue = entry._value;
removeEntry(entry);
return prevValue;
}
entry = entry._next;
}
return null;
}
/**
* Removes all mappings from this {@link FastMap}.
*/
public void clear() {
// Clears all keys, values and buckets linked lists.
for (EntryImpl entry = _mapFirst; entry != null; entry = entry._after) {
entry._key = null;
entry._value = null;
entry._before = null;
entry._next = null;
if (entry._previous == null) { // First in bucket.
_entries[entry._index] = null;
} else {
entry._previous = null;
}
}
// Recycles all entries.
if (_mapLast != null) {
_mapLast._after = _poolFirst; // Connects to pool.
_poolFirst = _mapFirst;
_mapFirst = null;
_mapLast = null;
_size = 0;
sizeChanged();
}
}
/**
* Changes the current capacity of this {@link FastMap}. If the capacity
* is increased, new entries are allocated and added to the pool.
* If the capacity is decreased, entries from the pool are deallocated
* (and are eventually garbage collected). The capacity also determined
* the number of buckets for the hash table.
*
* @param newCapacity the new capacity of this map.
*/
public void setCapacity(int newCapacity) {
if (newCapacity > _capacity) { // Capacity increases.
for (int i = _capacity; i < newCapacity; i++) {
EntryImpl entry = new EntryImpl();
entry._after = _poolFirst;
_poolFirst = entry;
}
} else if (newCapacity < _capacity) { // Capacity decreases.
for ( int i = newCapacity;
(i < _capacity) && (_poolFirst != null); i++) {
// Disconnects the entry for gc to do its work.
EntryImpl entry = _poolFirst;
_poolFirst = entry._after;
entry._after = null; // All pointers are now null!
}
}
// Find a power of 2 >= capacity
int tableLength = 16;
while (tableLength < newCapacity) {
tableLength <<= 1;
}
// Checks if the hash table has to be re-sized.
if (_entries.length != tableLength) {
_entries = new EntryImpl[tableLength];
_mask = tableLength - 1;
// Repopulates the hash table.
EntryImpl entry = _mapFirst;
while (entry != null) {
int index = keyHash(entry._key) & _mask;
entry._index = index;
// Connects to bucket.
entry._previous = null; // Resets previous.
EntryImpl next = _entries[index];
entry._next = next;
if (next != null) {
next._previous = entry;
}
_entries[index] = entry;
entry = entry._after;
}
}
_capacity = newCapacity;
}
/**
* Returns a shallow copy of this {@link FastMap}. The keys and
* the values themselves are not cloned.
*
* @return a shallow copy of this map.
*/
public Object clone() {
try {
FastMap clone = (FastMap) super.clone();
clone.initialize(_capacity);
clone.putAll(this);
return clone;
} catch (CloneNotSupportedException e) {
// Should not happen, since we are Cloneable.
throw new InternalError();
}
}
/**
* Compares the specified object with this {@link FastMap} for equality.
* Returns true
if the given object is also a map and the two
* maps represent the same mappings (regardless of collection iteration
* order).
*
* @param obj the object to be compared for equality with this map.
* @return true
if the specified object is equal to this map;
* false
otherwise.
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof Map) {
Map that = (Map) obj;
if (this.size() == that.size()) {
EntryImpl entry = _mapFirst;
while (entry != null) {
if (!that.entrySet().contains(entry)) {
return false;
}
entry = entry._after;
}
return true;
} else {
return false;
}
} else {
return false;
}
}
/**
* Returns the hash code value for this {@link FastMap}.
*
* @return the hash code value for this map.
*/
public int hashCode() {
int code = 0;
EntryImpl entry = _mapFirst;
while (entry != null) {
code += entry.hashCode();
entry = entry._after;
}
return code;
}
/**
* Returns a String
representation of this {@link FastMap}.
*
* @return this.entrySet().toString();
*/
public String toString() {
return entrySet().toString();
}
/**
* Returns a collection view of the values contained in this
* {@link FastMap}. The collection is backed by the map, so changes to
* the map are reflected in the collection, and vice-versa.
* The collection supports element removal, which removes the corresponding
* mapping from this map, via the
* Iterator.remove
, Collection.remove
,
* removeAll
, retainAll
,
* and clear
operations. It does not support the
* add
or addAll
operations.
*
* @return a collection view of the values contained in this map.
*/
public Collection values() {
return _values;
}
private transient Values _values;
private class Values extends AbstractCollection {
public Iterator iterator() {
return new Iterator() {
EntryImpl after = _mapFirst;
EntryImpl before;
public void remove() {
removeEntry(before);
}
public boolean hasNext() {
return after != null;
}
public Object next() {
before = after;
after = after._after;
return before._value;
}
};
}
public int size() {
return _size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
FastMap.this.clear();
}
}
/**
* Returns a collection view of the mappings contained in this
* {@link FastMap}. Each element in the returned collection is a
* Map.Entry
. The collection is backed by the map,
* so changes to the map are reflected in the collection, and vice-versa.
* The collection supports element removal, which removes the corresponding
* mapping from this map, via the
* Iterator.remove
, Collection.remove
,
* removeAll
, retainAll
,
* and clear
operations. It does not support the
* add
or addAll
operations.
*
* @return a collection view of the mappings contained in this map.
*/
public Set entrySet() {
return _entrySet;
}
private transient EntrySet _entrySet;
private class EntrySet extends AbstractSet {
public Iterator iterator() {
return new Iterator() {
EntryImpl after = _mapFirst;
EntryImpl before;
public void remove() {
removeEntry(before);
}
public boolean hasNext() {
return after != null;
}
public Object next() {
before = after;
after = after._after;
return before;
}
};
}
public int size() {
return _size;
}
public boolean contains(Object obj) { // Optimization.
if (obj instanceof Map.Entry) {
Map.Entry entry = (Map.Entry) obj;
Map.Entry mapEntry = getEntry(entry.getKey());
return entry.equals(mapEntry);
} else {
return false;
}
}
public boolean remove(Object obj) { // Optimization.
if (obj instanceof Map.Entry) {
Map.Entry entry = (Map.Entry)obj;
EntryImpl mapEntry = (EntryImpl) getEntry(entry.getKey());
if ((mapEntry != null) &&
(entry.getValue()).equals(mapEntry._value)) {
removeEntry(mapEntry);
return true;
}
}
return false;
}
}
/**
* Returns a set view of the keys contained in this {@link FastMap}.
* The set is backed by the map, so changes to the map are reflected
* in the set, and vice-versa. The set supports element removal,
* which removes the corresponding mapping from this map, via the
* Iterator.remove
, Collection.remove
,
* removeAll
, retainAll
,
* and clear
operations. It does not support the
* add
or addAll
operations.
*
* @return a set view of the keys contained in this map.
*/
public Set keySet() {
return _keySet;
}
private transient KeySet _keySet;
private class KeySet extends AbstractSet {
public Iterator iterator() {
return new Iterator() {
EntryImpl after = _mapFirst;
EntryImpl before;
public void remove() {
removeEntry(before);
}
public boolean hasNext() {
return after != null;
}
public Object next() {
before = after;
after = after._after;
return before._key;
}
};
}
public int size() {
return _size;
}
public boolean contains(Object obj) { // Optimization.
return FastMap.this.containsKey(obj);
}
public boolean remove(Object obj) { // Optimization.
return FastMap.this.remove(obj) != null;
}
public void clear() { // Optimization.
FastMap.this.clear();
}
}
/**
* This methods is being called when the size of this {@link FastMap}
* has changed. The default behavior is to double the map's capacity
* when the map's size reaches the current map's capacity.
* Sub-class may override this method to implement custom resizing
* policies or to disable automatic resizing. For example:
* Map fixedCapacityMap = new FastMap(256) {
* protected sizeChanged() {
* // Do nothing, automatic resizing disabled.
* }
* };
* @see #setCapacity
*/
protected void sizeChanged() {
if (size() > capacity()) {
setCapacity(capacity() * 2);
}
}
/**
* Returns the hash code for the specified key. The formula being used
* is identical to the formula used by java.util.HashMap
* (ensures similar behavior for ill-conditioned hashcode keys).
*
* @param key the key to calculate the hashcode for.
* @return the hash code for the specified key.
*/
private static int keyHash(Object key) {
// From HashMap.hash(Object) function.
int hashCode = key.hashCode();
hashCode += ~(hashCode << 9);
hashCode ^= (hashCode >>> 14);
hashCode += (hashCode << 4);
hashCode ^= (hashCode >>> 10);
return hashCode;
}
/**
* Adds a new entry for the specified key and value.
* @param key the entry's key.
* @param value the entry's value.
*/
private void addEntry(Object key, Object value) {
EntryImpl entry = _poolFirst;
if (entry != null) {
_poolFirst = entry._after;
entry._after = null;
} else { // Pool empty.
entry = new EntryImpl();
}
// Setup entry paramters.
entry._key = key;
entry._value = value;
int index = keyHash(key) & _mask;
entry._index = index;
// Connects to bucket.
EntryImpl next = _entries[index];
entry._next = next;
if (next != null) {
next._previous = entry;
}
_entries[index] = entry;
// Connects to collection.
if (_mapLast != null) {
entry._before = _mapLast;
_mapLast._after = entry;
} else {
_mapFirst = entry;
}
_mapLast = entry;
// Updates size.
_size++;
sizeChanged();
}
/**
* Removes the specified entry from the map.
*
* @param entry the entry to be removed.
*/
private void removeEntry(EntryImpl entry) {
// Removes from bucket.
EntryImpl previous = entry._previous;
EntryImpl next = entry._next;
if (previous != null) {
previous._next = next;
entry._previous = null;
} else { // First in bucket.
_entries[entry._index] = next;
}
if (next != null) {
next._previous = previous;
entry._next = null;
} // Else do nothing, no last pointer.
// Removes from collection.
EntryImpl before = entry._before;
EntryImpl after = entry._after;
if (before != null) {
before._after = after;
entry._before = null;
} else { // First in collection.
_mapFirst = after;
}
if (after != null) {
after._before = before;
} else { // Last in collection.
_mapLast = before;
}
// Clears value and key.
entry._key = null;
entry._value = null;
// Recycles.
entry._after = _poolFirst;
_poolFirst = entry;
// Updates size.
_size--;
sizeChanged();
}
/**
* Initializes this instance for the specified capacity.
* Once initialized, operations on this map should not create new objects
* (unless the map's size exceeds the specified capacity).
*
* @param capacity the initial capacity.
*/
private void initialize(int capacity) {
// Find a power of 2 >= capacity
int tableLength = 16;
while (tableLength < capacity) {
tableLength <<= 1;
}
// Allocates hash table.
_entries = new EntryImpl[tableLength];
_mask = tableLength - 1;
_capacity = capacity;
_size = 0;
// Allocates views.
_values = new Values();
_entrySet = new EntrySet();
_keySet = new KeySet();
// Resets pointers.
_poolFirst = null;
_mapFirst = null;
_mapLast = null;
// Allocates entries.
for (int i=0; i < capacity; i++) {
EntryImpl entry = new EntryImpl();
entry._after = _poolFirst;
_poolFirst = entry;
}
}
/**
* Requires special handling during de-serialization process.
*
* @param stream the object input stream.
* @throws IOException if an I/O error occurs.
* @throws ClassNotFoundException if the class for the object de-serialized
* is not found.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
int capacity = stream.readInt();
initialize(capacity);
int size = stream.readInt();
for (int i=0; i < size; i++) {
Object key = stream.readObject();
Object value = stream.readObject();
addEntry(key, value);
}
}
/**
* Requires special handling during serialization process.
*
* @param stream the object output stream.
* @throws IOException if an I/O error occurs.
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.writeInt(_capacity);
stream.writeInt(_size);
int count = 0;
EntryImpl entry = _mapFirst;
while (entry != null) {
stream.writeObject(entry._key);
stream.writeObject(entry._value);
count++;
entry = entry._after;
}
if (count != _size) {
throw new IOException("FastMap Corrupted");
}
}
/**
* This class represents a {@link FastMap} entry.
*/
private static final class EntryImpl implements Map.Entry {
/**
* Holds the entry key (null when in pool).
*/
private Object _key;
/**
* Holds the entry value (null when in pool).
*/
private Object _value;
/**
* Holds the bucket index (undefined when in pool).
*/
private int _index;
/**
* Holds the previous entry in the same bucket (null when in pool).
*/
private EntryImpl _previous;
/**
* Holds the next entry in the same bucket (null when in pool).
*/
private EntryImpl _next;
/**
* Holds the entry added before this entry (null when in pool).
*/
private EntryImpl _before;
/**
* Holds the entry added after this entry
* or the next available entry when in pool.
*/
private EntryImpl _after;
/**
* Returns the key for this entry.
*
* @return the entry's key.
*/
public Object getKey() {
return _key;
}
/**
* Returns the value for this entry.
*
* @return the entry's value.
*/
public Object getValue() {
return _value;
}
/**
* Sets the value for this entry.
*
* @param value the new value.
* @return the previous value.
*/
public Object setValue(Object value) {
Object old = _value;
_value = value;
return old;
}
/**
* Indicates if this entry is considered equals to the specified
* entry.
*
* @param that the object to test for equality.
* @return true
if both entry are considered equal;
*
false
otherwise.
*/
public boolean equals(Object that) {
if (that instanceof Map.Entry) {
Map.Entry entry = (Map.Entry) that;
return (_key.equals(entry.getKey())) &&
((_value != null) ?
_value.equals(entry.getValue()) :
(entry.getValue() == null));
} else {
return false;
}
}
/**
* Returns the hash code for this entry.
*
* @return this entry's hash code.
*/
public int hashCode() {
return _key.hashCode() ^ ((_value != null) ? _value.hashCode() : 0);
}
/**
* Returns the text representation of this entry.
*
* @return this entry's textual representation.
*/
public String toString() {
return _key + "=" + _value;
}
}
} plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/FileUtils.java 0000664 0000000 0000000 00000220531 11660305702 0026337 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.codehaus.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Turbine" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact codehaus@codehaus.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*
Path-related methods
*
* /www/hosted/mysite/index.html
, can be broken into:
*
*
* There are also methods to {@link #catPath concatenate two paths}, {@link #resolveFile resolve a
* path relative to a File} and {@link #normalize} a path.
* /www/hosted/mysite/
-- retrievable through {@link #getPath}index.html
-- retrievable through {@link #removePath}/www/hosted/mysite/index
-- retrievable through {@link #removeExtension}html
-- retrievable through {@link #getExtension}
":", "*", "?", "\"", "<", ">", "|"
*
* @see
* http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13
*/
private static final String[] INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME = { ":", "*", "?", "\"", "<", ">", "|" };
/**
* @return the default excludes pattern
* @see DirectoryScanner#DEFAULTEXCLUDES
*/
public static String[] getDefaultExcludes()
{
return DirectoryScanner.DEFAULTEXCLUDES;
}
/**
* @return the default excludes pattern as list.
* @see #getDefaultExcludes()
*/
public static List getDefaultExcludesAsList()
{
return Arrays.asList( getDefaultExcludes() );
}
/**
* @return the default excludes pattern as comma separated string.
* @see DirectoryScanner#DEFAULTEXCLUDES
* @see StringUtils#join(Object[], String)
*/
public static String getDefaultExcludesAsString()
{
return StringUtils.join( DirectoryScanner.DEFAULTEXCLUDES, "," );
}
/**
* Returns a human-readable version of the file size (original is in
* bytes).
*
* @param size The number of bytes.
* @return A human-readable display value (includes units).
*/
public static String byteCountToDisplaySize( int size )
{
String displaySize;
if ( size / ONE_GB > 0 )
{
displaySize = String.valueOf( size / ONE_GB ) + " GB";
}
else if ( size / ONE_MB > 0 )
{
displaySize = String.valueOf( size / ONE_MB ) + " MB";
}
else if ( size / ONE_KB > 0 )
{
displaySize = String.valueOf( size / ONE_KB ) + " KB";
}
else
{
displaySize = String.valueOf( size ) + " bytes";
}
return displaySize;
}
/**
* Returns the directory path portion of a file specification string.
* Matches the equally named unix command.
*
* @param filename the file path
* @return The directory portion excluding the ending file separator.
*/
public static String dirname( String filename )
{
int i = filename.lastIndexOf( File.separator );
return ( i >= 0 ? filename.substring( 0, i ) : "" );
}
/**
* Returns the filename portion of a file specification string.
*
* @param filename the file path
* @return The filename string with extension.
*/
public static String filename( String filename )
{
int i = filename.lastIndexOf( File.separator );
return ( i >= 0 ? filename.substring( i + 1 ) : filename );
}
/**
* Returns the filename portion of a file specification string.
* Matches the equally named unix command.
*
* @param filename the file path
* @return The filename string without extension.
*/
public static String basename( String filename )
{
return basename( filename, extension( filename ) );
}
/**
* Returns the filename portion of a file specification string.
* Matches the equally named unix command.
*
* @param filename the file path
* @param suffix the file suffix
* @return the basename of the file
*/
public static String basename( String filename, String suffix )
{
int i = filename.lastIndexOf( File.separator ) + 1;
int lastDot = ( ( suffix != null ) && ( suffix.length() > 0 ) ) ? filename.lastIndexOf( suffix ) : -1;
if ( lastDot >= 0 )
{
return filename.substring( i, lastDot );
}
else if ( i > 0 )
{
return filename.substring( i );
}
else
{
return filename; // else returns all (no path and no extension)
}
}
/**
* Returns the extension portion of a file specification string.
* This everything after the last dot '.' in the filename (NOT including
* the dot).
*
* @param filename the file path
* @return the extension of the file
*/
public static String extension( String filename )
{
// Ensure the last dot is after the last file separator
int lastSep = filename.lastIndexOf( File.separatorChar );
int lastDot;
if ( lastSep < 0 )
{
lastDot = filename.lastIndexOf( '.' );
}
else
{
lastDot = filename.substring( lastSep + 1 ).lastIndexOf( '.' );
if ( lastDot >= 0 )
{
lastDot += lastSep + 1;
}
}
if ( lastDot >= 0 && lastDot > lastSep)
{
return filename.substring( lastDot + 1 );
}
return "";
}
/**
* Check if a file exits.
*
* @param fileName the file path.
* @return true if file exists.
*/
public static boolean fileExists( String fileName )
{
File file = new File( fileName );
return file.exists();
}
/**
* Note: the file content is read with platform encoding.
*
* @param file the file path
* @return the file content using the platform encoding.
* @throws IOException if any
*/
public static String fileRead( String file )
throws IOException
{
return fileRead( file, null );
}
/**
* @param file the file path
* @param encoding the wanted encoding
* @return the file content using the specified encoding.
* @throws IOException if any
*/
public static String fileRead( String file, String encoding )
throws IOException
{
return fileRead( new File( file ), encoding );
}
/**
* Note: the file content is read with platform encoding
*
* @param file the file path
* @return the file content using the platform encoding.
* @throws IOException if any
*/
public static String fileRead( File file )
throws IOException
{
return fileRead( file, null);
}
/**
* @param file the file path
* @param encoding the wanted encoding
* @return the file content using the specified encoding.
* @throws IOException if any
*/
public static String fileRead( File file, String encoding )
throws IOException
{
StringBuffer buf = new StringBuffer();
Reader reader = null;
try
{
if ( encoding != null )
{
reader = new InputStreamReader( new FileInputStream( file ), encoding );
}
else
{
reader = new InputStreamReader( new FileInputStream( file ) );
}
int count;
char[] b = new char[512];
while ( ( count = reader.read( b ) ) > 0 ) // blocking read
{
buf.append( b, 0, count );
}
}
finally
{
IOUtil.close( reader );
}
return buf.toString();
}
/**
* Appends data to a file. The file will be created if it does not exist.
* Note: the data is written with platform encoding
*
* @param fileName The path of the file to write.
* @param data The content to write to the file.
* @throws IOException if any
*/
public static void fileAppend( String fileName, String data )
throws IOException
{
fileAppend( fileName, null, data);
}
/**
* Appends data to a file. The file will be created if it does not exist.
*
* @param fileName The path of the file to write.
* @param encoding The encoding of the file.
* @param data The content to write to the file.
* @throws IOException if any
*/
public static void fileAppend( String fileName, String encoding, String data )
throws IOException
{
FileOutputStream out = null;
try
{
out = new FileOutputStream( fileName, true );
if ( encoding != null ) {
out.write( data.getBytes( encoding ) );
}
else
{
out.write( data.getBytes() );
}
}
finally
{
IOUtil.close( out );
}
}
/**
* Writes data to a file. The file will be created if it does not exist.
* Note: the data is written with platform encoding
*
* @param fileName The path of the file to write.
* @param data The content to write to the file.
* @throws IOException if any
*/
public static void fileWrite( String fileName, String data )
throws IOException
{
fileWrite( fileName, null, data );
}
/**
* Writes data to a file. The file will be created if it does not exist.
*
* @param fileName The path of the file to write.
* @param encoding The encoding of the file.
* @param data The content to write to the file.
* @throws IOException if any
*/
public static void fileWrite( String fileName, String encoding, String data )
throws IOException
{
FileOutputStream out = null;
try
{
out = new FileOutputStream( fileName );
if ( encoding != null )
{
out.write( data.getBytes( encoding ) );
}
else
{
out.write( data.getBytes() );
}
}
finally
{
IOUtil.close( out );
}
}
/**
* Deletes a file.
*
* @param fileName The path of the file to delete.
*/
public static void fileDelete( String fileName )
{
File file = new File( fileName );
file.delete();
}
/**
* Waits for NFS to propagate a file creation, imposing a timeout.
*
* @param fileName The path of the file.
* @param seconds The maximum time in seconds to wait.
* @return True if file exists.
*/
public static boolean waitFor( String fileName, int seconds )
{
return waitFor( new File( fileName ), seconds );
}
/**
* Waits for NFS to propagate a file creation, imposing a timeout.
*
* @param file The file.
* @param seconds The maximum time in seconds to wait.
* @return True if file exists.
*/
public static boolean waitFor( File file, int seconds )
{
int timeout = 0;
int tick = 0;
while ( !file.exists() )
{
if ( tick++ >= 10 )
{
tick = 0;
if ( timeout++ > seconds )
{
return false;
}
}
try
{
Thread.sleep( 100 );
}
catch ( InterruptedException ignore )
{
// nop
}
}
return true;
}
/**
* Creates a file handle.
*
* @param fileName The path of the file.
* @return A File
manager.
*/
public static File getFile( String fileName )
{
return new File( fileName );
}
/**
* Given a directory and an array of extensions return an array of compliant files.
*
* TODO Should an ignore list be passed in?
* TODO Should a recurse flag be passed in?
*
* The given extensions should be like "java" and not like ".java"
*
* @param directory The path of the directory.
* @param extensions an array of expected extensions.
* @return An array of files for the wanted extensions.
*/
public static String[] getFilesFromExtension( String directory, String[] extensions )
{
Vector files = new Vector();
File currentDir = new File( directory );
String[] unknownFiles = currentDir.list();
if ( unknownFiles == null )
{
return new String[0];
}
for ( int i = 0; i < unknownFiles.length; ++i )
{
String currentFileName = directory + System.getProperty( "file.separator" ) + unknownFiles[i];
File currentFile = new File( currentFileName );
if ( currentFile.isDirectory() )
{
//ignore all CVS directories...
if ( currentFile.getName().equals( "CVS" ) )
{
continue;
}
//ok... transverse into this directory and get all the files... then combine
//them with the current list.
String[] fetchFiles = getFilesFromExtension( currentFileName, extensions );
files = blendFilesToVector( files, fetchFiles );
}
else
{
//ok... add the file
String add = currentFile.getAbsolutePath();
if ( isValidFile( add, extensions ) )
{
files.addElement( add );
}
}
}
//ok... move the Vector into the files list...
String[] foundFiles = new String[files.size()];
files.copyInto( foundFiles );
return foundFiles;
}
/**
* Private helper method for getFilesFromExtension()
*/
private static Vector blendFilesToVector( Vector v, String[] files )
{
for ( int i = 0; i < files.length; ++i )
{
v.addElement( files[i] );
}
return v;
}
/**
* Checks to see if a file is of a particular type(s).
* Note that if the file does not have an extension, an empty string
* ("") is matched for.
*/
private static boolean isValidFile( String file, String[] extensions )
{
String extension = extension( file );
if ( extension == null )
{
extension = "";
}
//ok.. now that we have the "extension" go through the current know
//excepted extensions and determine if this one is OK.
for ( int i = 0; i < extensions.length; ++i )
{
if ( extensions[i].equals( extension ) )
{
return true;
}
}
return false;
}
/**
* Simple way to make a directory
*
* @param dir the directory to create
* @throws IllegalArgumentException if the dir contains illegal Windows characters under Windows OS.
* @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME
*/
public static void mkdir( String dir )
{
File file = new File( dir );
if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
{
if ( !isValidWindowsFileName( file ) )
{
throw new IllegalArgumentException( "The file (" + dir
+ ") cannot contain any of the following characters: \n"
+ StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) );
}
}
if ( !file.exists() )
{
file.mkdirs();
}
}
/**
* Compare the contents of two files to determine if they are equal or not.
*
* @param file1 the first file
* @param file2 the second file
* @return true if the content of the files are equal or they both don't exist, false otherwise
* @throws IOException if any
*/
public static boolean contentEquals( final File file1, final File file2 )
throws IOException
{
final boolean file1Exists = file1.exists();
if ( file1Exists != file2.exists() )
{
return false;
}
if ( !file1Exists )
{
// two not existing files are equal
return true;
}
if ( file1.isDirectory() || file2.isDirectory() )
{
// don't want to compare directory contents
return false;
}
InputStream input1 = null;
InputStream input2 = null;
try
{
input1 = new FileInputStream( file1 );
input2 = new FileInputStream( file2 );
return IOUtil.contentEquals( input1, input2 );
}
finally
{
IOUtil.close( input1 );
IOUtil.close( input2 );
}
}
/**
* Convert from a URL
to a File
.
*
* @param url File URL.
* @return The equivalent File
object, or null
if the URL's protocol
* is not file
*/
public static File toFile( final URL url )
{
if ( url == null || !url.getProtocol().equalsIgnoreCase( "file" ) )
{
return null;
}
String filename = url.getFile().replace( '/', File.separatorChar );
int pos = -1;
while ( ( pos = filename.indexOf( '%', pos + 1 ) ) >= 0 )
{
if ( pos + 2 < filename.length() )
{
String hexStr = filename.substring( pos + 1, pos + 3 );
char ch = (char) Integer.parseInt( hexStr, 16 );
filename = filename.substring( 0, pos ) + ch + filename.substring( pos + 3 );
}
}
return new File( filename );
}
/**
* Convert the array of Files into a list of URLs.
*
* @param files the array of files
* @return the array of URLs
* @throws IOException if an error occurs
*/
public static URL[] toURLs( final File[] files )
throws IOException
{
final URL[] urls = new URL[files.length];
for ( int i = 0; i < urls.length; i++ )
{
urls[i] = files[i].toURL();
}
return urls;
}
/**
* Remove extension from filename.
* ie
* * foo.txt --> foo * a\b\c.jpg --> a\b\c * a\b\c --> a\b\c ** * @param filename the path of the file * @return the filename minus extension */ public static String removeExtension( final String filename ) { String ext = extension(filename); if ( "".equals(ext) ) { return filename; } final int index = filename.lastIndexOf( ext ) - 1; return filename.substring( 0, index ); } /** * Get extension from filename. * ie *
* foo.txt --> "txt" * a\b\c.jpg --> "jpg" * a\b\c --> "" ** * @param filename the path of the file * @return the extension of filename or "" if none */ public static String getExtension( final String filename ) { return extension(filename); } /** * Remove path from filename. Equivalent to the unix command
basename
* ie.
* * a/b/c.txt --> c.txt * a.txt --> a.txt ** * @param filepath the path of the file * @return the filename minus path */ public static String removePath( final String filepath ) { return removePath( filepath, File.separatorChar ); } /** * Remove path from filename. * ie. *
* a/b/c.txt --> c.txt * a.txt --> a.txt ** * @param filepath the path of the file * @param fileSeparatorChar the file separator character like / on Unix plateforms. * @return the filename minus path */ public static String removePath( final String filepath, final char fileSeparatorChar ) { final int index = filepath.lastIndexOf( fileSeparatorChar ); if ( -1 == index ) { return filepath; } return filepath.substring( index + 1 ); } /** * Get path from filename. Roughly equivalent to the unix command
dirname
.
* ie.
* * a/b/c.txt --> a/b * a.txt --> "" ** * @param filepath the filepath * @return the filename minus path */ public static String getPath( final String filepath ) { return getPath( filepath, File.separatorChar ); } /** * Get path from filename. * ie. *
* a/b/c.txt --> a/b * a.txt --> "" ** * @param filepath the filepath * @param fileSeparatorChar the file separator character like / on Unix plateforms. * @return the filename minus path */ public static String getPath( final String filepath, final char fileSeparatorChar ) { final int index = filepath.lastIndexOf( fileSeparatorChar ); if ( -1 == index ) { return ""; } return filepath.substring( 0, index ); } /** * Copy file from source to destination. If
destinationDirectory
does not exist, it
* (and any parent directories) will be created. If a file source
in
* destinationDirectory
exists, it will be overwritten.
*
* @param source An existing File
to copy.
* @param destinationDirectory A directory to copy source
into.
* @throws java.io.FileNotFoundException if source
isn't a normal file.
* @throws IllegalArgumentException if destinationDirectory
isn't a directory.
* @throws IOException if source
does not exist, the file in
* destinationDirectory
cannot be written to, or an IO error occurs during copying.
*/
public static void copyFileToDirectory( final String source, final String destinationDirectory )
throws IOException
{
copyFileToDirectory( new File( source ), new File( destinationDirectory ) );
}
/**
* Copy file from source to destination only if source is newer than the target file.
* If destinationDirectory
does not exist, it
* (and any parent directories) will be created. If a file source
in
* destinationDirectory
exists, it will be overwritten.
*
* @param source An existing File
to copy.
* @param destinationDirectory A directory to copy source
into.
* @throws java.io.FileNotFoundException if source
isn't a normal file.
* @throws IllegalArgumentException if destinationDirectory
isn't a directory.
* @throws IOException if source
does not exist, the file in
* destinationDirectory
cannot be written to, or an IO error occurs during copying.
*/
public static void copyFileToDirectoryIfModified( final String source, final String destinationDirectory )
throws IOException
{
copyFileToDirectoryIfModified( new File( source ), new File( destinationDirectory ) );
}
/**
* Copy file from source to destination. If destinationDirectory
does not exist, it
* (and any parent directories) will be created. If a file source
in
* destinationDirectory
exists, it will be overwritten.
*
* @param source An existing File
to copy.
* @param destinationDirectory A directory to copy source
into.
* @throws java.io.FileNotFoundException if source
isn't a normal file.
* @throws IllegalArgumentException if destinationDirectory
isn't a directory.
* @throws IOException if source
does not exist, the file in
* destinationDirectory
cannot be written to, or an IO error occurs during copying.
*/
public static void copyFileToDirectory( final File source, final File destinationDirectory )
throws IOException
{
if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() )
{
throw new IllegalArgumentException( "Destination is not a directory" );
}
copyFile( source, new File( destinationDirectory, source.getName() ) );
}
/**
* Copy file from source to destination only if source is newer than the target file.
* If destinationDirectory
does not exist, it
* (and any parent directories) will be created. If a file source
in
* destinationDirectory
exists, it will be overwritten.
*
* @param source An existing File
to copy.
* @param destinationDirectory A directory to copy source
into.
* @throws java.io.FileNotFoundException if source
isn't a normal file.
* @throws IllegalArgumentException if destinationDirectory
isn't a directory.
* @throws IOException if source
does not exist, the file in
* destinationDirectory
cannot be written to, or an IO error occurs during copying.
*/
public static void copyFileToDirectoryIfModified( final File source, final File destinationDirectory )
throws IOException
{
if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() )
{
throw new IllegalArgumentException( "Destination is not a directory" );
}
copyFileIfModified( source, new File( destinationDirectory, source.getName() ) );
}
/**
* Copy file from source to destination. The directories up to destination
will be
* created if they don't already exist. destination
will be overwritten if it
* already exists.
*
* @param source An existing non-directory File
to copy bytes from.
* @param destination A non-directory File
to write bytes to (possibly
* overwriting).
* @throws IOException if source
does not exist, destination
cannot be
* written to, or an IO error occurs during copying.
* @throws java.io.FileNotFoundException if destination
is a directory
* (use {@link #copyFileToDirectory}).
*/
public static void copyFile( final File source, final File destination )
throws IOException
{
//check source exists
if ( !source.exists() )
{
final String message = "File " + source + " does not exist";
throw new IOException( message );
}
//check source != destination, see PLXUTILS-10
if ( source.getCanonicalPath().equals( destination.getCanonicalPath() ) )
{
//if they are equal, we can exit the method without doing any work
return;
}
copyStreamToFile( new FileInputStreamFacade( source ), destination);
if ( source.length() != destination.length() )
{
final String message = "Failed to copy full contents from " + source + " to " + destination;
throw new IOException( message );
}
}
/**
* Copy file from source to destination only if source timestamp is later than the destination timestamp.
* The directories up to destination
will be created if they don't already exist.
* destination
will be overwritten if it already exists.
*
* @param source An existing non-directory File
to copy bytes from.
* @param destination A non-directory File
to write bytes to (possibly
* overwriting).
* @return true if no problem occured
* @throws IOException if source
does not exist, destination
cannot be
* written to, or an IO error occurs during copying.
* @throws FileNotFoundException if destination
is a directory
* (use {@link #copyFileToDirectory}).
*/
public static boolean copyFileIfModified( final File source, final File destination )
throws IOException
{
if ( destination.lastModified() < source.lastModified() )
{
copyFile( source, destination );
return true;
}
return false;
}
/**
* Copies bytes from the URL source
to a file destination
.
* The directories up to destination
will be created if they don't already exist.
* destination
will be overwritten if it already exists.
*
* @param source A URL
to copy bytes from.
* @param destination A non-directory File
to write bytes to (possibly
* overwriting).
* @throws IOException if
* source
URL cannot be openeddestination
cannot be written tosource
to a file destination
.
* The directories up to destination
will be created if they don't already exist.
* destination
will be overwritten if it already exists.
*
* @param source An {@link InputStream} to copy bytes from. This stream is
* guaranteed to be closed.
* @param destination A non-directory File
to write bytes to (possibly
* overwriting).
* @throws IOException if
* source
URL cannot be openeddestination
cannot be written tonull
if the ..'s went past the
* root.
* Eg:
* * /foo// --> /foo/ * /foo/./ --> /foo/ * /foo/../bar --> /bar * /foo/../bar/ --> /bar/ * /foo/../bar/../baz --> /baz * //foo//./bar --> /foo/bar * /../ --> null ** * @param path the path to normalize * @return the normalized String, or
null
if too many ..'s.
*/
public static String normalize( final String path )
{
String normalized = path;
// Resolve occurrences of "//" in the normalized path
while ( true )
{
int index = normalized.indexOf( "//" );
if ( index < 0 )
{
break;
}
normalized = normalized.substring( 0, index ) + normalized.substring( index + 1 );
}
// Resolve occurrences of "/./" in the normalized path
while ( true )
{
int index = normalized.indexOf( "/./" );
if ( index < 0 )
{
break;
}
normalized = normalized.substring( 0, index ) + normalized.substring( index + 2 );
}
// Resolve occurrences of "/../" in the normalized path
while ( true )
{
int index = normalized.indexOf( "/../" );
if ( index < 0 )
{
break;
}
if ( index == 0 )
{
return null; // Trying to go outside our context
}
int index2 = normalized.lastIndexOf( '/', index - 1 );
normalized = normalized.substring( 0, index2 ) + normalized.substring( index + 3 );
}
// Return the normalized path that we have completed
return normalized;
}
/**
* Will concatenate 2 paths. Paths with ..
will be
* properly handled.
* Eg.,
* /a/b/c
+ d
= /a/b/d
* /a/b/c
+ ../d
= /a/d
*
filename
to it's canonical form. If filename
is
* relative (doesn't start with /
), it will be resolved relative to
* baseFile
, otherwise it is treated as a normal root-relative path.
*
* @param baseFile Where to resolve filename
from, if filename
is
* relative.
* @param filename Absolute or relative file path to resolve.
* @return The canonical File
of filename
.
*/
public static File resolveFile( final File baseFile, String filename )
{
String filenm = filename;
if ( '/' != File.separatorChar )
{
filenm = filename.replace( '/', File.separatorChar );
}
if ( '\\' != File.separatorChar )
{
filenm = filename.replace( '\\', File.separatorChar );
}
// deal with absolute files
if ( filenm.startsWith( File.separator ) || ( Os.isFamily( Os.FAMILY_WINDOWS ) && filenm.indexOf( ":" ) > 0 ) )
{
File file = new File( filenm );
try
{
file = file.getCanonicalFile();
}
catch ( final IOException ioe )
{
// nop
}
return file;
}
// FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips
// them. However, I'm not sure about this UNC stuff. (JT)
final char[] chars = filename.toCharArray();
final StringBuffer sb = new StringBuffer();
//remove duplicate file separators in succession - except
//on win32 at start of filename as UNC filenames can
//be \\AComputer\AShare\myfile.txt
int start = 0;
if ( '\\' == File.separatorChar )
{
sb.append( filenm.charAt( 0 ) );
start++;
}
for ( int i = start; i < chars.length; i++ )
{
final boolean doubleSeparator = File.separatorChar == chars[i] && File.separatorChar == chars[i - 1];
if ( !doubleSeparator )
{
sb.append( chars[i] );
}
}
filenm = sb.toString();
//must be relative
File file = ( new File( baseFile, filenm ) ).getAbsoluteFile();
try
{
file = file.getCanonicalFile();
}
catch ( final IOException ioe )
{
// nop
}
return file;
}
/**
* Delete a file. If file is directory delete it and all sub-directories.
*
* @param file the file path
* @throws IOException if any
*/
public static void forceDelete( final String file )
throws IOException
{
forceDelete( new File( file ) );
}
/**
* Delete a file. If file is directory delete it and all sub-directories.
*
* @param file a file
* @throws IOException if any
*/
public static void forceDelete( final File file )
throws IOException
{
if ( file.isDirectory() )
{
deleteDirectory( file );
}
else
{
/*
* NOTE: Always try to delete the file even if it appears to be non-existent. This will ensure that a
* symlink whose target does not exist is deleted, too.
*/
boolean filePresent = file.getCanonicalFile().exists();
if ( !deleteFile( file ) && filePresent )
{
final String message = "File " + file + " unable to be deleted.";
throw new IOException( message );
}
}
}
/**
* Accommodate Windows bug encountered in both Sun and IBM JDKs.
* Others possible. If the delete does not work, call System.gc(),
* wait a little and try again.
*
* @param file a file
* @throws IOException if any
*/
private static boolean deleteFile( File file )
throws IOException
{
if ( file.isDirectory() )
{
throw new IOException( "File " + file + " isn't a file." );
}
if ( !file.delete() )
{
if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
{
file = file.getCanonicalFile();
System.gc();
}
try
{
Thread.sleep( 10 );
return file.delete();
}
catch ( InterruptedException ex )
{
return file.delete();
}
}
return true;
}
/**
* Schedule a file to be deleted when JVM exits.
* If file is directory delete it and all sub-directories.
*
* @param file a file
* @throws IOException if any
*/
public static void forceDeleteOnExit( final File file )
throws IOException
{
if ( !file.exists() )
{
return;
}
if ( file.isDirectory() )
{
deleteDirectoryOnExit( file );
}
else
{
file.deleteOnExit();
}
}
/**
* Recursively schedule directory for deletion on JVM exit.
*
* @param file a directory
* @throws IOException if any
*/
private static void deleteDirectoryOnExit( final File directory )
throws IOException
{
if ( !directory.exists() )
{
return;
}
cleanDirectoryOnExit( directory );
directory.deleteOnExit();
}
/**
* Clean a directory without deleting it.
*
* @param file a directory
* @throws IOException if any
*/
private static void cleanDirectoryOnExit( final File directory )
throws IOException
{
if ( !directory.exists() )
{
final String message = directory + " does not exist";
throw new IllegalArgumentException( message );
}
if ( !directory.isDirectory() )
{
final String message = directory + " is not a directory";
throw new IllegalArgumentException( message );
}
IOException exception = null;
final File[] files = directory.listFiles();
for ( int i = 0; i < files.length; i++ )
{
final File file = files[i];
try
{
forceDeleteOnExit( file );
}
catch ( final IOException ioe )
{
exception = ioe;
}
}
if ( null != exception )
{
throw exception;
}
}
/**
* Make a directory.
*
* @param file not null
* @throws IOException If there already exists a file with specified name or
* the directory is unable to be created
* @throws IllegalArgumentException if the file contains illegal Windows characters under Windows OS.
* @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME
*/
public static void forceMkdir( final File file )
throws IOException
{
if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
{
if ( !isValidWindowsFileName( file ) )
{
throw new IllegalArgumentException( "The file (" + file.getAbsolutePath()
+ ") cannot contain any of the following characters: \n"
+ StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) );
}
}
if ( file.exists() )
{
if ( file.isFile() )
{
final String message =
"File " + file + " exists and is " + "not a directory. Unable to create directory.";
throw new IOException( message );
}
}
else
{
if ( false == file.mkdirs() )
{
final String message = "Unable to create directory " + file;
throw new IOException( message );
}
}
}
/**
* Recursively delete a directory.
*
* @param directory a directory
* @throws IOException if any
*/
public static void deleteDirectory( final String directory )
throws IOException
{
deleteDirectory( new File( directory ) );
}
/**
* Recursively delete a directory.
*
* @param directory a directory
* @throws IOException if any
*/
public static void deleteDirectory( final File directory )
throws IOException
{
if ( !directory.exists() )
{
return;
}
cleanDirectory( directory );
if ( !directory.delete() )
{
final String message = "Directory " + directory + " unable to be deleted.";
throw new IOException( message );
}
}
/**
* Clean a directory without deleting it.
*
* @param directory a directory
* @throws IOException if any
*/
public static void cleanDirectory( final String directory )
throws IOException
{
cleanDirectory( new File( directory ) );
}
/**
* Clean a directory without deleting it.
*
* @param directory a directory
* @throws IOException if any
*/
public static void cleanDirectory( final File directory )
throws IOException
{
if ( !directory.exists() )
{
final String message = directory + " does not exist";
throw new IllegalArgumentException( message );
}
if ( !directory.isDirectory() )
{
final String message = directory + " is not a directory";
throw new IllegalArgumentException( message );
}
IOException exception = null;
final File[] files = directory.listFiles();
if ( files == null )
{
return;
}
for ( int i = 0; i < files.length; i++ )
{
final File file = files[i];
try
{
forceDelete( file );
}
catch ( final IOException ioe )
{
exception = ioe;
}
}
if ( null != exception )
{
throw exception;
}
}
/**
* Recursively count size of a directory.
*
* @param directory a directory
* @return size of directory in bytes.
*/
public static long sizeOfDirectory( final String directory )
{
return sizeOfDirectory( new File( directory ) );
}
/**
* Recursively count size of a directory.
*
* @param directory a directory
* @return size of directory in bytes.
*/
public static long sizeOfDirectory( final File directory )
{
if ( !directory.exists() )
{
final String message = directory + " does not exist";
throw new IllegalArgumentException( message );
}
if ( !directory.isDirectory() )
{
final String message = directory + " is not a directory";
throw new IllegalArgumentException( message );
}
long size = 0;
final File[] files = directory.listFiles();
for ( int i = 0; i < files.length; i++ )
{
final File file = files[i];
if ( file.isDirectory() )
{
size += sizeOfDirectory( file );
}
else
{
size += file.length();
}
}
return size;
}
/**
* Return the files contained in the directory, using inclusion and exclusion Ant patterns,
* including the directory name in each of the files
*
* @param directory the directory to scan
* @param includes the includes pattern, comma separated
* @param excludes the excludes pattern, comma separated
* @return a list of File objects
* @throws IOException
* @see #getFileNames( File, String, String, boolean )
*/
public static List getFiles( File directory, String includes, String excludes )
throws IOException
{
return getFiles( directory, includes, excludes, true );
}
/**
* Return the files contained in the directory, using inclusion and exclusion Ant patterns
*
* @param directory the directory to scan
* @param includes the includes pattern, comma separated
* @param excludes the excludes pattern, comma separated
* @param includeBasedir true to include the base dir in each file
* @return a list of File objects
* @throws IOException
* @see #getFileNames( File, String, String, boolean )
*/
public static List getFiles( File directory, String includes, String excludes, boolean includeBasedir )
throws IOException
{
List fileNames = getFileNames( directory, includes, excludes, includeBasedir );
List files = new ArrayList();
for ( Iterator i = fileNames.iterator(); i.hasNext(); )
{
files.add( new File( (String) i.next() ) );
}
return files;
}
/**
* Return a list of files as String depending options.
* This method use case sensitive file name.
*
* @param directory the directory to scan
* @param includes the includes pattern, comma separated
* @param excludes the excludes pattern, comma separated
* @param includeBasedir true to include the base dir in each String of file
* @return a list of files as String
* @throws IOException
*/
public static List getFileNames( File directory, String includes, String excludes, boolean includeBasedir )
throws IOException
{
return getFileNames( directory, includes, excludes, includeBasedir, true );
}
/**
* Return a list of files as String depending options.
*
* @param directory the directory to scan
* @param includes the includes pattern, comma separated
* @param excludes the excludes pattern, comma separated
* @param includeBasedir true to include the base dir in each String of file
* @param isCaseSensitive true if case sensitive
* @return a list of files as String
* @throws IOException
*/
public static List getFileNames( File directory, String includes, String excludes, boolean includeBasedir,
boolean isCaseSensitive )
throws IOException
{
return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, true, false );
}
/**
* Return a list of directories as String depending options.
* This method use case sensitive file name.
*
* @param directory the directory to scan
* @param includes the includes pattern, comma separated
* @param excludes the excludes pattern, comma separated
* @param includeBasedir true to include the base dir in each String of file
* @return a list of directories as String
* @throws IOException
*/
public static List getDirectoryNames( File directory, String includes, String excludes, boolean includeBasedir )
throws IOException
{
return getDirectoryNames( directory, includes, excludes, includeBasedir, true );
}
/**
* Return a list of directories as String depending options.
*
* @param directory the directory to scan
* @param includes the includes pattern, comma separated
* @param excludes the excludes pattern, comma separated
* @param includeBasedir true to include the base dir in each String of file
* @param isCaseSensitive true if case sensitive
* @return a list of directories as String
* @throws IOException
*/
public static List getDirectoryNames( File directory, String includes, String excludes, boolean includeBasedir,
boolean isCaseSensitive )
throws IOException
{
return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, false, true );
}
/**
* Return a list of files as String depending options.
*
* @param directory the directory to scan
* @param includes the includes pattern, comma separated
* @param excludes the excludes pattern, comma separated
* @param includeBasedir true to include the base dir in each String of file
* @param isCaseSensitive true if case sensitive
* @param getFiles true if get files
* @param getDirectories true if get directories
* @return a list of files as String
* @throws IOException
*/
public static List getFileAndDirectoryNames( File directory, String includes, String excludes,
boolean includeBasedir, boolean isCaseSensitive, boolean getFiles,
boolean getDirectories )
throws IOException
{
DirectoryScanner scanner = new DirectoryScanner();
scanner.setBasedir( directory );
if ( includes != null )
{
scanner.setIncludes( StringUtils.split( includes, "," ) );
}
if ( excludes != null )
{
scanner.setExcludes( StringUtils.split( excludes, "," ) );
}
scanner.setCaseSensitive( isCaseSensitive );
scanner.scan();
List list = new ArrayList();
if ( getFiles )
{
String[] files = scanner.getIncludedFiles();
for ( int i = 0; i < files.length; i++ )
{
if ( includeBasedir )
{
list.add( directory + FileUtils.FS + files[i] );
}
else
{
list.add( files[i] );
}
}
}
if ( getDirectories )
{
String[] directories = scanner.getIncludedDirectories();
for ( int i = 0; i < directories.length; i++ )
{
if ( includeBasedir )
{
list.add( directory + FileUtils.FS + directories[i] );
}
else
{
list.add( directories[i] );
}
}
}
return list;
}
/**
* Copy a directory to an other one.
*
* @param sourceDirectory the source dir
* @param destinationDirectory the target dir
* @throws IOException if any
*/
public static void copyDirectory( File sourceDirectory, File destinationDirectory )
throws IOException
{
copyDirectory( sourceDirectory, destinationDirectory, "**", null );
}
/**
* Copy a directory to an other one.
*
* @param sourceDirectory the source dir
* @param destinationDirectory the target dir
* @param includes include pattern
* @param excludes exlucde pattern
* @throws IOException if any
* @see #getFiles(File, String, String)
*/
public static void copyDirectory( File sourceDirectory, File destinationDirectory, String includes,
String excludes )
throws IOException
{
if ( !sourceDirectory.exists() )
{
return;
}
List files = getFiles( sourceDirectory, includes, excludes );
for ( Iterator i = files.iterator(); i.hasNext(); )
{
File file = (File) i.next();
copyFileToDirectory( file, destinationDirectory );
}
}
/**
* Copies a entire directory layout : no files will be copied only directories
*
* Note:
* sourceDirectory
must exists.
* sourceDirectory
must exists.
* sourceDirectory
must exists.
* This will remove to
(if it exists), ensure that
* to
's parent directory exists and move
* from
, which involves deleting from
as
* well.
to
may have been deleted already when this happens.
*/
public static void rename( File from, File to )
throws IOException
{
if ( to.exists() && !to.delete() )
{
throw new IOException( "Failed to delete " + to + " while trying to rename " + from );
}
File parent = to.getParentFile();
if ( parent != null && !parent.exists() && !parent.mkdirs() )
{
throw new IOException( "Failed to create directory " + parent + " while trying to rename " + from );
}
if ( !from.renameTo( to ) )
{
copyFile( from, to );
if ( !from.delete() )
{
throw new IOException( "Failed to delete " + from + " while trying to rename it." );
}
}
}
/**
* Create a temporary file in a given directory.
*
* The file denoted by the returned abstract pathname did not * exist before this method was invoked, any subsequent invocation * of this method will yield a different file name.
* * The filename is prefixNNNNNsuffix where NNNN is a random number * *This method is different to {@link File#createTempFile(String, String, File)} of JDK 1.2 * as it doesn't create the file itself. * It uses the location pointed to by java.io.tmpdir * when the parentDir attribute is * null.
*To delete automatically the file created by this method, use the * {@link File#deleteOnExit()} method.
* * @param prefix prefix before the random number * @param suffix file extension; include the '.' * @param parentDir Directory to create the temporary file in-java.io.tmpdir
* used if not specificed
* @return a File reference to the new temporary file.
*/
public static File createTempFile( String prefix, String suffix, File parentDir )
{
File result = null;
String parent = System.getProperty( "java.io.tmpdir" );
if ( parentDir != null )
{
parent = parentDir.getPath();
}
DecimalFormat fmt = new DecimalFormat( "#####" );
SecureRandom secureRandom = new SecureRandom();
long secureInitializer = secureRandom.nextLong();
Random rand = new Random( secureInitializer + Runtime.getRuntime().freeMemory() );
synchronized ( rand )
{
do
{
result = new File( parent, prefix + fmt.format( Math.abs( rand.nextInt() ) ) + suffix );
}
while ( result.exists() );
}
return result;
}
/**
* If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified()
* @param from the file to copy
* @param to the destination file
* @param encoding the file output encoding (only if wrappers is not empty)
* @param wrappers array of {@link FilterWrapper}
* @throws IOException if an IO error occurs during copying or filtering
*/
public static void copyFile( File from, File to, String encoding, FilterWrapper[] wrappers )
throws IOException
{
copyFile( from, to, encoding, wrappers, false );
}
public static abstract class FilterWrapper
{
public abstract Reader getReader( Reader fileReader );
}
/**
* If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified() or if overwrite is true
* @param from the file to copy
* @param to the destination file
* @param encoding the file output encoding (only if wrappers is not empty)
* @param wrappers array of {@link FilterWrapper}
* @param overwrite if true and f wrappers is null or empty, the file will be copy
* enven if to.lastModified() < from.lastModified()
* @throws IOException if an IO error occurs during copying or filtering
* @since 1.5.2
*/
public static void copyFile( File from, File to, String encoding, FilterWrapper[] wrappers, boolean overwrite )
throws IOException
{
if ( wrappers != null && wrappers.length > 0 )
{
// buffer so it isn't reading a byte at a time!
Reader fileReader = null;
Writer fileWriter = null;
try
{
if ( encoding == null || encoding.length() < 1 )
{
fileReader = new BufferedReader( new FileReader( from ) );
fileWriter = new FileWriter( to );
}
else
{
FileInputStream instream = new FileInputStream( from );
FileOutputStream outstream = new FileOutputStream( to );
fileReader = new BufferedReader( new InputStreamReader( instream, encoding ) );
fileWriter = new OutputStreamWriter( outstream, encoding );
}
Reader reader = fileReader;
for ( int i = 0; i < wrappers.length; i++ )
{
FilterWrapper wrapper = wrappers[i];
reader = wrapper.getReader( reader );
}
IOUtil.copy( reader, fileWriter );
}
finally
{
IOUtil.close( fileReader );
IOUtil.close( fileWriter );
}
}
else
{
if ( to.lastModified() < from.lastModified() || overwrite )
{
copyFile( from, to );
}
}
}
/**
* Note: the file content is read with platform encoding
*
* @param file the file
* @return a List containing every every line not starting with # and not empty
* @throws IOException if any
*/
public static List loadFile( File file )
throws IOException
{
List lines = new ArrayList();
if ( file.exists() )
{
BufferedReader reader = new BufferedReader( new FileReader( file ) );
String line = reader.readLine();
while ( line != null )
{
line = line.trim();
if ( !line.startsWith( "#" ) && line.length() != 0 )
{
lines.add( line );
}
line = reader.readLine();
}
reader.close();
}
return lines;
}
/**
* For Windows OS, check if the file name contains any of the following characters:
* ":", "*", "?", "\"", "<", ">", "|"
*
* @param f not null file
* @return false
if the file path contains any of forbidden Windows characters,
* true
if the Os is not Windows or if the file path respect the Windows constraints.
* @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME
* @since 1.5.2
*/
public static boolean isValidWindowsFileName( File f )
{
if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
{
if ( StringUtils.indexOfAny( f.getName(), INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME ) != -1 )
{
return false;
}
if ( f.getParentFile()!= null)
{
return isValidWindowsFileName( f.getParentFile() );
}
}
return true;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/IOUtil.java 0000664 0000000 0000000 00000070460 11660305702 0025610 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.codehaus.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Turbine" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact codehaus@codehaus.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*
* This class provides static utility methods for input/output operations, particularly buffered
* copying between sources (InputStream
, Reader
, String
and
* byte[]
) and destinations (OutputStream
, Writer
,
* String
and byte[]
).
*
Unless otherwise noted, these copy
methods do not flush or close the
* streams. Often, doing so would require making non-portable assumptions about the streams' origin
* and further use. This means that both streams' close()
methods must be called after
* copying. if one omits this step, then the stream resources (sockets, file descriptors) are
* released when the associated Stream is garbage-collected. It is not a good idea to rely on this
* mechanism. For a good overview of the distinction between "memory management" and "resource
* management", see this
* UnixReview article
For each copy
method, a variant is provided that allows the caller to specify the
* buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this
* may be worth tweaking. Often "large buffer -> faster" does not hold, even for large data
* transfers.
For byte-to-char methods, a copy
variant allows the encoding to be selected
* (otherwise the platform default is used).
The copy
methods use an internal buffer when copying. It is therefore advisable
* not to deliberately wrap the stream arguments to the copy
methods in
* Buffered*
streams. For example, don't do the
* following:
copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) );
*
* The rationale is as follows:
* *Imagine that an InputStream's read() is a very expensive operation, which would usually suggest
* wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent
* {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to
* fill an internal buffer, from which further read
requests can inexpensively get
* their data (until the buffer runs out).
However, the copy
methods do the same thing, keeping an internal buffer,
* populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers
* (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer
* management hurts performance slightly (about 3%, according to some simple experiments).
InputStream
to an OutputStream
.
*/
public static void copy( final InputStream input, final OutputStream output )
throws IOException
{
copy( input, output, DEFAULT_BUFFER_SIZE );
}
/**
* Copy bytes from an InputStream
to an OutputStream
.
* @param bufferSize Size of internal buffer to use.
*/
public static void copy( final InputStream input,
final OutputStream output,
final int bufferSize )
throws IOException
{
final byte[] buffer = new byte[bufferSize];
int n = 0;
while ( -1 != ( n = input.read( buffer ) ) )
{
output.write( buffer, 0, n );
}
}
/**
* Copy chars from a Reader
to a Writer
.
*/
public static void copy( final Reader input, final Writer output )
throws IOException
{
copy( input, output, DEFAULT_BUFFER_SIZE );
}
/**
* Copy chars from a Reader
to a Writer
.
* @param bufferSize Size of internal buffer to use.
*/
public static void copy( final Reader input, final Writer output, final int bufferSize )
throws IOException
{
final char[] buffer = new char[bufferSize];
int n = 0;
while ( -1 != ( n = input.read( buffer ) ) )
{
output.write( buffer, 0, n );
}
output.flush();
}
///////////////////////////////////////////////////////////////
// Derived copy methods
// InputStream -> *
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// InputStream -> Writer
/**
* Copy and convert bytes from an InputStream
to chars on a
* Writer
.
* The platform's default encoding is used for the byte-to-char conversion.
*/
public static void copy( final InputStream input, final Writer output )
throws IOException
{
copy( input, output, DEFAULT_BUFFER_SIZE );
}
/**
* Copy and convert bytes from an InputStream
to chars on a
* Writer
.
* The platform's default encoding is used for the byte-to-char conversion.
* @param bufferSize Size of internal buffer to use.
*/
public static void copy( final InputStream input, final Writer output, final int bufferSize )
throws IOException
{
final InputStreamReader in = new InputStreamReader( input );
copy( in, output, bufferSize );
}
/**
* Copy and convert bytes from an InputStream
to chars on a
* Writer
, using the specified encoding.
* @param encoding The name of a supported character encoding. See the
* IANA
* Charset Registry for a list of valid encoding types.
*/
public static void copy( final InputStream input, final Writer output, final String encoding )
throws IOException
{
final InputStreamReader in = new InputStreamReader( input, encoding );
copy( in, output );
}
/**
* Copy and convert bytes from an InputStream
to chars on a
* Writer
, using the specified encoding.
* @param encoding The name of a supported character encoding. See the
* IANA
* Charset Registry for a list of valid encoding types.
* @param bufferSize Size of internal buffer to use.
*/
public static void copy( final InputStream input,
final Writer output,
final String encoding,
final int bufferSize )
throws IOException
{
final InputStreamReader in = new InputStreamReader( input, encoding );
copy( in, output, bufferSize );
}
///////////////////////////////////////////////////////////////
// InputStream -> String
/**
* Get the contents of an InputStream
as a String.
* The platform's default encoding is used for the byte-to-char conversion.
*/
public static String toString( final InputStream input )
throws IOException
{
return toString( input, DEFAULT_BUFFER_SIZE );
}
/**
* Get the contents of an InputStream
as a String.
* The platform's default encoding is used for the byte-to-char conversion.
* @param bufferSize Size of internal buffer to use.
*/
public static String toString( final InputStream input, final int bufferSize )
throws IOException
{
final StringWriter sw = new StringWriter();
copy( input, sw, bufferSize );
return sw.toString();
}
/**
* Get the contents of an InputStream
as a String.
* @param encoding The name of a supported character encoding. See the
* IANA
* Charset Registry for a list of valid encoding types.
*/
public static String toString( final InputStream input, final String encoding )
throws IOException
{
return toString( input, encoding, DEFAULT_BUFFER_SIZE );
}
/**
* Get the contents of an InputStream
as a String.
* @param encoding The name of a supported character encoding. See the
* IANA
* Charset Registry for a list of valid encoding types.
* @param bufferSize Size of internal buffer to use.
*/
public static String toString( final InputStream input,
final String encoding,
final int bufferSize )
throws IOException
{
final StringWriter sw = new StringWriter();
copy( input, sw, encoding, bufferSize );
return sw.toString();
}
///////////////////////////////////////////////////////////////
// InputStream -> byte[]
/**
* Get the contents of an InputStream
as a byte[]
.
*/
public static byte[] toByteArray( final InputStream input )
throws IOException
{
return toByteArray( input, DEFAULT_BUFFER_SIZE );
}
/**
* Get the contents of an InputStream
as a byte[]
.
* @param bufferSize Size of internal buffer to use.
*/
public static byte[] toByteArray( final InputStream input, final int bufferSize )
throws IOException
{
final ByteArrayOutputStream output = new ByteArrayOutputStream();
copy( input, output, bufferSize );
return output.toByteArray();
}
///////////////////////////////////////////////////////////////
// Derived copy methods
// Reader -> *
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// Reader -> OutputStream
/**
* Serialize chars from a Reader
to bytes on an OutputStream
, and
* flush the OutputStream
.
*/
public static void copy( final Reader input, final OutputStream output )
throws IOException
{
copy( input, output, DEFAULT_BUFFER_SIZE );
}
/**
* Serialize chars from a Reader
to bytes on an OutputStream
, and
* flush the OutputStream
.
* @param bufferSize Size of internal buffer to use.
*/
public static void copy( final Reader input, final OutputStream output, final int bufferSize )
throws IOException
{
final OutputStreamWriter out = new OutputStreamWriter( output );
copy( input, out, bufferSize );
// NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
// here.
out.flush();
}
///////////////////////////////////////////////////////////////
// Reader -> String
/**
* Get the contents of a Reader
as a String.
*/
public static String toString( final Reader input )
throws IOException
{
return toString( input, DEFAULT_BUFFER_SIZE );
}
/**
* Get the contents of a Reader
as a String.
* @param bufferSize Size of internal buffer to use.
*/
public static String toString( final Reader input, final int bufferSize )
throws IOException
{
final StringWriter sw = new StringWriter();
copy( input, sw, bufferSize );
return sw.toString();
}
///////////////////////////////////////////////////////////////
// Reader -> byte[]
/**
* Get the contents of a Reader
as a byte[]
.
*/
public static byte[] toByteArray( final Reader input )
throws IOException
{
return toByteArray( input, DEFAULT_BUFFER_SIZE );
}
/**
* Get the contents of a Reader
as a byte[]
.
* @param bufferSize Size of internal buffer to use.
*/
public static byte[] toByteArray( final Reader input, final int bufferSize )
throws IOException
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
copy( input, output, bufferSize );
return output.toByteArray();
}
///////////////////////////////////////////////////////////////
// Derived copy methods
// String -> *
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// String -> OutputStream
/**
* Serialize chars from a String
to bytes on an OutputStream
, and
* flush the OutputStream
.
*/
public static void copy( final String input, final OutputStream output )
throws IOException
{
copy( input, output, DEFAULT_BUFFER_SIZE );
}
/**
* Serialize chars from a String
to bytes on an OutputStream
, and
* flush the OutputStream
.
* @param bufferSize Size of internal buffer to use.
*/
public static void copy( final String input, final OutputStream output, final int bufferSize )
throws IOException
{
final StringReader in = new StringReader( input );
final OutputStreamWriter out = new OutputStreamWriter( output );
copy( in, out, bufferSize );
// NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
// here.
out.flush();
}
///////////////////////////////////////////////////////////////
// String -> Writer
/**
* Copy chars from a String
to a Writer
.
*/
public static void copy( final String input, final Writer output )
throws IOException
{
output.write( input );
}
/**
* Copy bytes from an InputStream
to an
* OutputStream
, with buffering.
* This is equivalent to passing a
* {@link java.io.BufferedInputStream} and
* {@link java.io.BufferedOutputStream} to {@link #copy(InputStream, OutputStream)},
* and flushing the output stream afterwards. The streams are not closed
* after the copy.
* @deprecated Buffering streams is actively harmful! See the class description as to why. Use
* {@link #copy(InputStream, OutputStream)} instead.
*/
public static void bufferedCopy( final InputStream input, final OutputStream output )
throws IOException
{
final BufferedInputStream in = new BufferedInputStream( input );
final BufferedOutputStream out = new BufferedOutputStream( output );
copy( in, out );
out.flush();
}
///////////////////////////////////////////////////////////////
// String -> byte[]
/**
* Get the contents of a String
as a byte[]
.
*/
public static byte[] toByteArray( final String input )
throws IOException
{
return toByteArray( input, DEFAULT_BUFFER_SIZE );
}
/**
* Get the contents of a String
as a byte[]
.
* @param bufferSize Size of internal buffer to use.
*/
public static byte[] toByteArray( final String input, final int bufferSize )
throws IOException
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
copy( input, output, bufferSize );
return output.toByteArray();
}
///////////////////////////////////////////////////////////////
// Derived copy methods
// byte[] -> *
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// byte[] -> Writer
/**
* Copy and convert bytes from a byte[]
to chars on a
* Writer
.
* The platform's default encoding is used for the byte-to-char conversion.
*/
public static void copy( final byte[] input, final Writer output )
throws IOException
{
copy( input, output, DEFAULT_BUFFER_SIZE );
}
/**
* Copy and convert bytes from a byte[]
to chars on a
* Writer
.
* The platform's default encoding is used for the byte-to-char conversion.
* @param bufferSize Size of internal buffer to use.
*/
public static void copy( final byte[] input, final Writer output, final int bufferSize )
throws IOException
{
final ByteArrayInputStream in = new ByteArrayInputStream( input );
copy( in, output, bufferSize );
}
/**
* Copy and convert bytes from a byte[]
to chars on a
* Writer
, using the specified encoding.
* @param encoding The name of a supported character encoding. See the
* IANA
* Charset Registry for a list of valid encoding types.
*/
public static void copy( final byte[] input, final Writer output, final String encoding )
throws IOException
{
final ByteArrayInputStream in = new ByteArrayInputStream( input );
copy( in, output, encoding );
}
/**
* Copy and convert bytes from a byte[]
to chars on a
* Writer
, using the specified encoding.
* @param encoding The name of a supported character encoding. See the
* IANA
* Charset Registry for a list of valid encoding types.
* @param bufferSize Size of internal buffer to use.
*/
public static void copy( final byte[] input,
final Writer output,
final String encoding,
final int bufferSize )
throws IOException
{
final ByteArrayInputStream in = new ByteArrayInputStream( input );
copy( in, output, encoding, bufferSize );
}
///////////////////////////////////////////////////////////////
// byte[] -> String
/**
* Get the contents of a byte[]
as a String.
* The platform's default encoding is used for the byte-to-char conversion.
*/
public static String toString( final byte[] input )
throws IOException
{
return toString( input, DEFAULT_BUFFER_SIZE );
}
/**
* Get the contents of a byte[]
as a String.
* The platform's default encoding is used for the byte-to-char conversion.
* @param bufferSize Size of internal buffer to use.
*/
public static String toString( final byte[] input, final int bufferSize )
throws IOException
{
final StringWriter sw = new StringWriter();
copy( input, sw, bufferSize );
return sw.toString();
}
/**
* Get the contents of a byte[]
as a String.
* @param encoding The name of a supported character encoding. See the
* IANA
* Charset Registry for a list of valid encoding types.
*/
public static String toString( final byte[] input, final String encoding )
throws IOException
{
return toString( input, encoding, DEFAULT_BUFFER_SIZE );
}
/**
* Get the contents of a byte[]
as a String.
* @param encoding The name of a supported character encoding. See the
* IANA
* Charset Registry for a list of valid encoding types.
* @param bufferSize Size of internal buffer to use.
*/
public static String toString( final byte[] input,
final String encoding,
final int bufferSize )
throws IOException
{
final StringWriter sw = new StringWriter();
copy( input, sw, encoding, bufferSize );
return sw.toString();
}
///////////////////////////////////////////////////////////////
// byte[] -> OutputStream
/**
* Copy bytes from a byte[]
to an OutputStream
.
*/
public static void copy( final byte[] input, final OutputStream output )
throws IOException
{
copy( input, output, DEFAULT_BUFFER_SIZE );
}
/**
* Copy bytes from a byte[]
to an OutputStream
.
* @param bufferSize Size of internal buffer to use.
*/
public static void copy( final byte[] input,
final OutputStream output,
final int bufferSize )
throws IOException
{
output.write( input );
}
/**
* Compare the contents of two Streams to determine if they are equal or not.
*
* @param input1 the first stream
* @param input2 the second stream
* @return true if the content of the streams are equal or they both don't exist, false otherwise
*/
public static boolean contentEquals( final InputStream input1,
final InputStream input2 )
throws IOException
{
final InputStream bufferedInput1 = new BufferedInputStream( input1 );
final InputStream bufferedInput2 = new BufferedInputStream( input2 );
int ch = bufferedInput1.read();
while ( -1 != ch )
{
final int ch2 = bufferedInput2.read();
if ( ch != ch2 )
{
return false;
}
ch = bufferedInput1.read();
}
final int ch2 = bufferedInput2.read();
if ( -1 != ch2 )
{
return false;
}
else
{
return true;
}
}
// ----------------------------------------------------------------------
// closeXXX()
// ----------------------------------------------------------------------
/**
* Closes the input stream. The input stream can be null and any IOException's will be swallowed.
*
* @param inputStream The stream to close.
*/
public static void close( InputStream inputStream )
{
if ( inputStream == null )
{
return;
}
try
{
inputStream.close();
}
catch( IOException ex )
{
// ignore
}
}
/**
* Closes the output stream. The output stream can be null and any IOException's will be swallowed.
*
* @param outputStream The stream to close.
*/
public static void close( OutputStream outputStream )
{
if ( outputStream == null )
{
return;
}
try
{
outputStream.close();
}
catch( IOException ex )
{
// ignore
}
}
/**
* Closes the reader. The reader can be null and any IOException's will be swallowed.
*
* @param reader The reader to close.
*/
public static void close( Reader reader )
{
if ( reader == null )
{
return;
}
try
{
reader.close();
}
catch( IOException ex )
{
// ignore
}
}
/**
* Closes the writer. The writer can be null and any IOException's will be swallowed.
*
* @param writer The writer to close.
*/
public static void close( Writer writer )
{
if ( writer == null )
{
return;
}
try
{
writer.close();
}
catch( IOException ex )
{
// ignore
}
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/InterpolationFilterReader.java 0000664 0000000 0000000 00000023716 11660305702 0031565 0 ustar 00root root 0000000 0000000 /*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.codehaus.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "Ant" and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact codehaus@codehaus.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* n
is negative.
* @exception IOException If an I/O error occurs
*/
public long skip( long n ) throws IOException
{
if ( n < 0L )
{
throw new IllegalArgumentException( "skip value is negative" );
}
for ( long i = 0; i < n; i++ )
{
if ( read() == -1 )
{
return i;
}
}
return n;
}
/**
* Reads characters into a portion of an array. This method will block
* until some input is available, an I/O error occurs, or the end of the
* stream is reached.
*
* @param cbuf Destination buffer to write characters to.
* Must not be null
.
* @param off Offset at which to start storing characters.
* @param len Maximum number of characters to read.
*
* @return the number of characters read, or -1 if the end of the
* stream has been reached
*
* @exception IOException If an I/O error occurs
*/
public int read( char cbuf[],
int off,
int len )
throws IOException
{
for ( int i = 0; i < len; i++ )
{
int ch = read();
if ( ch == -1 )
{
if ( i == 0 )
{
return -1;
}
else
{
return i;
}
}
cbuf[off + i] = (char) ch;
}
return len;
}
/**
* Returns the next character in the filtered stream, replacing tokens
* from the original stream.
*
* @return the next character in the resulting stream, or -1
* if the end of the resulting stream has been reached
*
* @exception IOException if the underlying stream throws an IOException
* during reading
*/
public int read() throws IOException
{
if ( replaceIndex != -1 && replaceIndex < replaceData.length() )
{
int ch = replaceData.charAt( replaceIndex++ );
if ( replaceIndex >= replaceData.length() )
{
replaceIndex = -1;
}
return ch;
}
int ch = -1;
if ( previousIndex != -1 && previousIndex < endTokenLength )
{
ch = endToken.charAt( previousIndex++ );
}
else
{
ch = in.read();
}
if ( ch == beginToken.charAt( 0 ) )
{
StringBuffer key = new StringBuffer();
int beginTokenMatchPos = 1;
do
{
if ( previousIndex != -1 && previousIndex < endTokenLength )
{
ch = endToken.charAt( previousIndex++ );
}
else
{
ch = in.read();
}
if ( ch != -1 )
{
key.append( (char) ch );
if ( ( beginTokenMatchPos < beginTokenLength ) &&
( ch != beginToken.charAt( beginTokenMatchPos++ ) ) )
{
ch = -1; // not really EOF but to trigger code below
break;
}
}
else
{
break;
}
}
while ( ch != endToken.charAt( 0 ) );
// now test endToken
if ( ch != -1 && endTokenLength > 1 )
{
int endTokenMatchPos = 1;
do
{
if ( previousIndex != -1 && previousIndex < endTokenLength )
{
ch = endToken.charAt( previousIndex++ );
}
else
{
ch = in.read();
}
if ( ch != -1 )
{
key.append( (char) ch );
if ( ch != endToken.charAt( endTokenMatchPos++ ) )
{
ch = -1; // not really EOF but to trigger code below
break;
}
}
else
{
break;
}
}
while ( endTokenMatchPos < endTokenLength );
}
// There is nothing left to read so we have the situation where the begin/end token
// are in fact the same and as there is nothing left to read we have got ourselves
// end of a token boundary so let it pass through.
if ( ch == -1 )
{
replaceData = key.toString();
replaceIndex = 0;
return beginToken.charAt( 0 );
}
String variableKey = key.substring( beginTokenLength - 1, key.length() - endTokenLength );
Object o = variables.get(variableKey);
if ( o != null )
{
String value = o.toString();
if ( value.length() != 0 )
{
replaceData = value;
replaceIndex = 0;
}
return read();
}
else
{
previousIndex = 0;
replaceData = key.substring(0, key.length() - endTokenLength );
replaceIndex = 0;
return beginToken.charAt(0);
}
}
return ch;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/LineOrientedInterpolatingReader.java 0000664 0000000 0000000 00000026500 11660305702 0032703 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import org.codehaus.plexus.util.reflection.Reflector;
import org.codehaus.plexus.util.reflection.ReflectorException;
import java.io.FilterReader;
import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* @author jdcasey Created on Feb 3, 2005
*/
public class LineOrientedInterpolatingReader
extends FilterReader
{
public static final String DEFAULT_START_DELIM = "${";
public static final String DEFAULT_END_DELIM = "}";
public static final String DEFAULT_ESCAPE_SEQ = "\\";
private static final char CARRIAGE_RETURN_CHAR = '\r';
private static final char NEWLINE_CHAR = '\n';
private final PushbackReader pushbackReader;
private final Map context;
private final String startDelim;
private final String endDelim;
private final String escapeSeq;
private final int minExpressionSize;
private final Reflector reflector;
private int lineIdx = -1;
private String line;
public LineOrientedInterpolatingReader( Reader reader, Map context, String startDelim, String endDelim,
String escapeSeq )
{
super( reader );
this.startDelim = startDelim;
this.endDelim = endDelim;
this.escapeSeq = escapeSeq;
// Expressions have to be at least this size...
this.minExpressionSize = startDelim.length() + endDelim.length() + 1;
this.context = Collections.unmodifiableMap( context );
this.reflector = new Reflector();
if ( reader instanceof PushbackReader )
{
this.pushbackReader = (PushbackReader) reader;
}
else
{
this.pushbackReader = new PushbackReader( reader, 1 );
}
}
public LineOrientedInterpolatingReader( Reader reader, Map context, String startDelim, String endDelim )
{
this( reader, context, startDelim, endDelim, DEFAULT_ESCAPE_SEQ );
}
public LineOrientedInterpolatingReader( Reader reader, Map context )
{
this( reader, context, DEFAULT_START_DELIM, DEFAULT_END_DELIM, DEFAULT_ESCAPE_SEQ );
}
public int read() throws IOException
{
if ( line == null || lineIdx >= line.length() )
{
readAndInterpolateLine();
}
int next = -1;
if ( line != null && lineIdx < line.length() )
{
next = line.charAt( lineIdx++ );
}
return next;
}
public int read( char[] cbuf, int off, int len ) throws IOException
{
int fillCount = 0;
for ( int i = off; i < off + len; i++ )
{
int next = read();
if ( next > -1 )
{
cbuf[i] = (char) next;
}
else
{
break;
}
fillCount++;
}
if ( fillCount == 0 )
{
fillCount = -1;
}
return fillCount;
}
public long skip( long n ) throws IOException
{
long skipCount = 0;
for ( long i = 0; i < n; i++ )
{
int next = read();
if ( next < 0 )
{
break;
}
skipCount++;
}
return skipCount;
}
private void readAndInterpolateLine() throws IOException
{
String rawLine = readLine();
if(rawLine != null)
{
Set expressions = parseForExpressions( rawLine );
Map evaluatedExpressions = evaluateExpressions( expressions );
String interpolated = replaceWithInterpolatedValues( rawLine, evaluatedExpressions );
if ( interpolated != null && interpolated.length() > 0 )
{
line = interpolated;
lineIdx = 0;
}
}
else
{
line = null;
lineIdx = -1;
}
}
private String readLine() throws IOException
{
StringBuffer lineBuffer = new StringBuffer( 40 ); // half of the "normal" line maxsize
int next = -1;
boolean lastWasCR = false;
while ( ( next = pushbackReader.read() ) > -1 )
{
char c = (char) next;
if ( c == CARRIAGE_RETURN_CHAR )
{
lastWasCR = true;
lineBuffer.append( c );
}
else if ( c == NEWLINE_CHAR )
{
lineBuffer.append( c );
break; // end of line.
}
else if ( lastWasCR )
{
pushbackReader.unread( c );
break;
}
else
{
lineBuffer.append( c );
}
}
if ( lineBuffer.length() < 1 )
{
return null;
}
else
{
return lineBuffer.toString();
}
}
private String replaceWithInterpolatedValues( String rawLine, Map evaluatedExpressions )
{
String result = rawLine;
for ( Iterator it = evaluatedExpressions.entrySet().iterator(); it.hasNext(); )
{
Map.Entry entry = (Map.Entry) it.next();
String expression = (String) entry.getKey();
String value = String.valueOf( entry.getValue() );
result = findAndReplaceUnlessEscaped( result, expression, value );
}
return result;
}
private Map evaluateExpressions( Set expressions )
{
Map evaluated = new TreeMap();
for ( Iterator it = expressions.iterator(); it.hasNext(); )
{
String rawExpression = (String) it.next();
String realExpression = rawExpression.substring( startDelim.length(), rawExpression.length()
- endDelim.length() );
String[] parts = realExpression.split( "\\." );
if ( parts.length > 0 )
{
Object value = context.get( parts[0] );
if ( value != null )
{
for ( int i = 1; i < parts.length; i++ )
{
try
{
value = reflector.getObjectProperty( value, parts[i] );
if ( value == null )
{
break;
}
}
catch ( ReflectorException e )
{
// TODO: Fix this! It should report, but not interrupt.
e.printStackTrace();
break;
}
}
evaluated.put( rawExpression, value );
}
}
}
return evaluated;
}
private Set parseForExpressions( String rawLine )
{
Set expressions = new HashSet();
if ( rawLine != null )
{
int placeholder = -1;
do
{
// find the beginning of the next expression.
int start = findDelimiter( rawLine, startDelim, placeholder );
// if we can't find a start-delimiter, then there is no valid expression. Ignore everything else.
if ( start < 0 )
{
// no expression found.
break;
}
// find the end of the next expression.
int end = findDelimiter( rawLine, endDelim, start + 1 );
// if we can't find an end-delimiter, then this is not a valid expression. Ignore it.
if ( end < 0 )
{
// no VALID expression found.
break;
}
// if we reach this point, we have a valid start and end position, which
// means we have a valid expression. So, we add it to the set of
// expressions in need of evaluation.
expressions.add( rawLine.substring( start, end + endDelim.length() ) );
// increment the placeholder so we can look beyond this expression.
placeholder = end + 1;
} while ( placeholder < rawLine.length() - minExpressionSize );
}
return expressions;
}
private int findDelimiter( String rawLine, String delimiter, int lastPos )
{
int placeholder = lastPos;
int position = -1;
do
{
position = rawLine.indexOf( delimiter, placeholder );
if ( position < 0 )
{
break;
}
else
{
int escEndIdx = rawLine.indexOf( escapeSeq, placeholder ) + escapeSeq.length();
if ( escEndIdx > escapeSeq.length() - 1 && escEndIdx == position )
{
placeholder = position + 1;
position = -1;
}
}
} while ( position < 0 && placeholder < rawLine.length() - endDelim.length() );
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// use length() - endDelim.length() b/c otherwise there is nothing left to search.
return position;
}
private String findAndReplaceUnlessEscaped(String rawLine, String search, String replace)
{
StringBuffer lineBuffer = new StringBuffer( (int)(rawLine.length() * 1.5) );
int lastReplacement = -1;
do
{
int nextReplacement = rawLine.indexOf( search, lastReplacement + 1 );
if(nextReplacement > -1)
{
if(lastReplacement < 0)
{
lastReplacement = 0;
}
lineBuffer.append( rawLine.substring( lastReplacement, nextReplacement ) );
int escIdx = rawLine.indexOf( escapeSeq, lastReplacement + 1 );
if(escIdx > -1 && escIdx + escapeSeq.length() == nextReplacement)
{
lineBuffer.setLength( lineBuffer.length() - escapeSeq.length() );
lineBuffer.append( search );
}
else
{
lineBuffer.append( replace );
}
lastReplacement = nextReplacement + search.length();
}
else
{
break;
}
}
while(lastReplacement > -1);
if( lastReplacement < rawLine.length() )
{
lineBuffer.append( rawLine.substring( lastReplacement ) );
}
return lineBuffer.toString();
}
} plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/Os.java 0000664 0000000 0000000 00000031733 11660305702 0025024 0 ustar 00root root 0000000 0000000 /*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "Ant" and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* $relativePath
context variable. The arguments to
* this method may contain either forward or backward slashes as
* file separators. The relative path returned is formed using
* forward slashes as it is expected this path is to be used as a
* link in a web page (again mimicking Anakia's behavior).
*
* This method is thread-safe.
* * PathTool.getRelativePath( null, null ) = "" * PathTool.getRelativePath( null, "/usr/local/java/bin" ) = "" * PathTool.getRelativePath( "/usr/local/", null ) = "" * PathTool.getRelativePath( "/usr/local/", "/usr/local/java/bin" ) = ".." * PathTool.getRelativePath( "/usr/local/", "/usr/local/java/bin/java.sh" ) = "../.." * PathTool.getRelativePath( "/usr/local/java/bin/java.sh", "/usr/local/" ) = "" ** * @param basedir The base directory. * @param filename The filename that is relative to the base * directory. * @return The relative path of the filename from the base * directory. This value is not terminated with a forward slash. * A zero-length string is returned if: the filename is not relative to * the base directory,
basedir
is null or zero-length,
* or filename
is null or zero-length.
*/
public static final String getRelativePath( String basedir, String filename )
{
basedir = uppercaseDrive(basedir);
filename = uppercaseDrive(filename);
/*
* Verify the arguments and make sure the filename is relative
* to the base directory.
*/
if ( basedir == null || basedir.length() == 0 || filename == null
|| filename.length() == 0 || !filename.startsWith( basedir ) )
{
return "";
}
/*
* Normalize the arguments. First, determine the file separator
* that is being used, then strip that off the end of both the
* base directory and filename.
*/
String separator = determineSeparator( filename );
basedir = StringUtils.chompLast( basedir, separator );
filename = StringUtils.chompLast( filename, separator );
/*
* Remove the base directory from the filename to end up with a
* relative filename (relative to the base directory). This
* filename is then used to determine the relative path.
*/
String relativeFilename = filename.substring( basedir.length() );
return determineRelativePath( relativeFilename, separator );
}
/**
* Determines the relative path of a filename. This method is
* useful in building relative links within pages of a web site. It
* provides similar functionality to Anakia's
* $relativePath
context variable. The argument to
* this method may contain either forward or backward slashes as
* file separators. The relative path returned is formed using
* forward slashes as it is expected this path is to be used as a
* link in a web page (again mimicking Anakia's behavior).
*
* This method is thread-safe.
*
* @param filename The filename to be parsed.
* @return The relative path of the filename. This value is not
* terminated with a forward slash. A zero-length string is
* returned if: filename
is null or zero-length.
* @see #getRelativeFilePath(String, String)
*/
public static final String getRelativePath( String filename )
{
filename = uppercaseDrive(filename);
if ( filename == null || filename.length() == 0 )
{
return "";
}
/*
* Normalize the argument. First, determine the file separator
* that is being used, then strip that off the end of the
* filename. Then, if the filename doesn't begin with a
* separator, add one.
*/
String separator = determineSeparator( filename );
filename = StringUtils.chompLast( filename, separator );
if ( !filename.startsWith( separator ) )
{
filename = separator + filename;
}
return determineRelativePath( filename, separator );
}
/**
* Determines the directory component of a filename. This is useful
* within DVSL templates when used in conjunction with the DVSL's
* $context.getAppValue("infilename")
to get the
* current directory that is currently being processed.
*
* This method is thread-safe.
* * PathTool.getDirectoryComponent( null ) = "" * PathTool.getDirectoryComponent( "/usr/local/java/bin" ) = "/usr/local/java" * PathTool.getDirectoryComponent( "/usr/local/java/bin/" ) = "/usr/local/java/bin" * PathTool.getDirectoryComponent( "/usr/local/java/bin/java.sh" ) = "/usr/local/java/bin" ** * @param filename The filename to be parsed. * @return The directory portion of the
filename
. If
* the filename does not contain a directory component, "." is
* returned.
*/
public static final String getDirectoryComponent( String filename )
{
if ( filename == null || filename.length() == 0 )
{
return "";
}
String separator = determineSeparator( filename );
String directory = StringUtils.chomp( filename, separator );
if ( filename.equals( directory ) )
{
return ".";
}
return directory;
}
/**
* Calculates the appropriate link given the preferred link and the relativePath of the document.
* * PathTool.calculateLink( "/index.html", "../.." ) = "../../index.html" * PathTool.calculateLink( "http://plexus.codehaus.org/plexus-utils/index.html", "../.." ) = "http://plexus.codehaus.org/plexus-utils/index.html" * PathTool.calculateLink( "/usr/local/java/bin/java.sh", "../.." ) = "../../usr/local/java/bin/java.sh" * PathTool.calculateLink( "../index.html", "/usr/local/java/bin" ) = "/usr/local/java/bin/../index.html" * PathTool.calculateLink( "../index.html", "http://plexus.codehaus.org/plexus-utils" ) = "http://plexus.codehaus.org/plexus-utils/../index.html" ** * @param link * @param relativePath * @return String */ public static final String calculateLink(String link, String relativePath) { //This must be some historical feature if (link.startsWith("/site/")) { return link.substring(5); } //Allows absolute links in nav-bars etc if (link.startsWith("/absolute/")) { return link.substring(10); } // This traps urls like http:// if (link.indexOf(":") >= 0) { return link; } //If relativepath is current directory, just pass the link through if (relativePath.equals(".")) { if (link.startsWith("/")) { return link.substring(1); } return link; } //If we don't do this, you can end up with ..//bob.html rather than ../bob.html if (relativePath.endsWith("/") && link.startsWith("/")) { return relativePath + "." + link.substring(1); } if (relativePath.endsWith("/") || link.startsWith("/")) { return relativePath + link; } return relativePath + "/" + link; } /** * This method can calculate the relative path between two pathes on a web site. *
* PathTool.getRelativeWebPath( null, null ) = "" * PathTool.getRelativeWebPath( null, "http://plexus.codehaus.org/" ) = "" * PathTool.getRelativeWebPath( "http://plexus.codehaus.org/", null ) = "" * PathTool.getRelativeWebPath( "http://plexus.codehaus.org/", * "http://plexus.codehaus.org/plexus-utils/index.html" ) = "plexus-utils/index.html" * PathTool.getRelativeWebPath( "http://plexus.codehaus.org/plexus-utils/index.html", * "http://plexus.codehaus.org/" = "../../" ** * @param oldPath * @param newPath * @return a relative web path from
oldPath
.
*/
public static final String getRelativeWebPath( final String oldPath, final String newPath )
{
if ( StringUtils.isEmpty( oldPath ) || StringUtils.isEmpty( newPath ) )
{
return "";
}
String resultPath = buildRelativePath( newPath, oldPath, '/' );
if ( newPath.endsWith( "/" ) && !resultPath.endsWith( "/" ) )
{
return resultPath + "/";
}
return resultPath;
}
/**
* This method can calculate the relative path between two pathes on a file system.
* * PathTool.getRelativeFilePath( null, null ) = "" * PathTool.getRelativeFilePath( null, "/usr/local/java/bin" ) = "" * PathTool.getRelativeFilePath( "/usr/local", null ) = "" * PathTool.getRelativeFilePath( "/usr/local", "/usr/local/java/bin" ) = "java/bin" * PathTool.getRelativeFilePath( "/usr/local", "/usr/local/java/bin/" ) = "java/bin" * PathTool.getRelativeFilePath( "/usr/local/java/bin", "/usr/local/" ) = "../.." * PathTool.getRelativeFilePath( "/usr/local/", "/usr/local/java/bin/java.sh" ) = "java/bin/java.sh" * PathTool.getRelativeFilePath( "/usr/local/java/bin/java.sh", "/usr/local/" ) = "../../.." * PathTool.getRelativeFilePath( "/usr/local/", "/bin" ) = "../../bin" * PathTool.getRelativeFilePath( "/bin", "/usr/local/" ) = "../usr/local" ** Note: On Windows based system, the
/
character should be replaced by \
character.
*
* @param oldPath
* @param newPath
* @return a relative file path from oldPath
.
*/
public static final String getRelativeFilePath( final String oldPath, final String newPath )
{
if ( StringUtils.isEmpty( oldPath ) || StringUtils.isEmpty( newPath ) )
{
return "";
}
// normalise the path delimiters
String fromPath = new File( oldPath ).getPath();
String toPath = new File( newPath ).getPath();
// strip any leading slashes if its a windows path
if ( toPath.matches( "^\\[a-zA-Z]:" ) )
{
toPath = toPath.substring( 1 );
}
if ( fromPath.matches( "^\\[a-zA-Z]:" ) )
{
fromPath = fromPath.substring( 1 );
}
// lowercase windows drive letters.
if ( fromPath.startsWith( ":", 1 ) )
{
fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 );
}
if ( toPath.startsWith( ":", 1 ) )
{
toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 );
}
// check for the presence of windows drives. No relative way of
// traversing from one to the other.
if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) )
&& ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) )
{
// they both have drive path element but they dont match, no
// relative path
return null;
}
if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) )
|| ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) )
{
// one has a drive path element and the other doesnt, no relative
// path.
return null;
}
String resultPath = buildRelativePath( toPath, fromPath, File.separatorChar );
if ( newPath.endsWith( File.separator ) && !resultPath.endsWith( File.separator ) )
{
return resultPath + File.separator;
}
return resultPath;
}
// ----------------------------------------------------------------------
// Private methods
// ----------------------------------------------------------------------
/**
* Determines the relative path of a filename. For each separator
* within the filename (except the leading if present), append the
* "../" string to the return value.
*
* @param filename The filename to parse.
* @param separator The separator used within the filename.
* @return The relative path of the filename. This value is not
* terminated with a forward slash. A zero-length string is
* returned if: the filename is zero-length.
*/
private static final String determineRelativePath( String filename,
String separator )
{
if ( filename.length() == 0 )
{
return "";
}
/*
* Count the slashes in the relative filename, but exclude the
* leading slash. If the path has no slashes, then the filename
* is relative to the current directory.
*/
int slashCount = StringUtils.countMatches( filename, separator ) - 1;
if ( slashCount <= 0 )
{
return ".";
}
/*
* The relative filename contains one or more slashes indicating
* that the file is within one or more directories. Thus, each
* slash represents a "../" in the relative path.
*/
StringBuffer sb = new StringBuffer();
for ( int i = 0; i < slashCount; i++ )
{
sb.append( "../" );
}
/*
* Finally, return the relative path but strip the trailing
* slash to mimic Anakia's behavior.
*/
return StringUtils.chop( sb.toString() );
}
/**
* Helper method to determine the file separator (forward or
* backward slash) used in a filename. The slash that occurs more
* often is returned as the separator.
*
* @param filename The filename parsed to determine the file
* separator.
* @return The file separator used within filename
.
* This value is either a forward or backward slash.
*/
private static final String determineSeparator( String filename )
{
int forwardCount = StringUtils.countMatches( filename, "/" );
int backwardCount = StringUtils.countMatches( filename, "\\" );
return forwardCount >= backwardCount ? "/" : "\\";
}
/**
* Cygwin prefers lowercase drive letters, but other parts of maven use uppercase
* @param path
* @return String
*/
static final String uppercaseDrive(String path)
{
if (path == null)
{
return null;
}
if (path.length() >= 2 && path.charAt(1) == ':')
{
path = Character.toUpperCase( path.charAt( 0 ) ) + path.substring( 1 );
}
return path;
}
private static final String buildRelativePath( String toPath, String fromPath, final char separatorChar )
{
// use tokeniser to traverse paths and for lazy checking
StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) );
StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) );
int count = 0;
// walk along the to path looking for divergence from the from path
while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() )
{
if ( separatorChar == '\\' )
{
if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) )
{
break;
}
}
else
{
if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) )
{
break;
}
}
count++;
}
// reinitialise the tokenisers to count positions to retrieve the
// gobbled token
toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) );
fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) );
while ( count-- > 0 )
{
fromTokeniser.nextToken();
toTokeniser.nextToken();
}
String relativePath = "";
// add back refs for the rest of from location.
while ( fromTokeniser.hasMoreTokens() )
{
fromTokeniser.nextToken();
relativePath += "..";
if ( fromTokeniser.hasMoreTokens() )
{
relativePath += separatorChar;
}
}
if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() )
{
relativePath += separatorChar;
}
// add fwd fills for whatevers left of newPath.
while ( toTokeniser.hasMoreTokens() )
{
relativePath += toTokeniser.nextToken();
if ( toTokeniser.hasMoreTokens() )
{
relativePath += separatorChar;
}
}
return relativePath;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/PropertyUtils.java 0000664 0000000 0000000 00000004341 11660305702 0027303 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.util.Properties;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
/**
*
*
* @author Jason van Zyl
* @author Michal Maczka
*
* @version $Id: PropertyUtils.java 8010 2009-01-07 12:59:50Z vsiveton $
*/
public class PropertyUtils
{
public static Properties loadProperties( URL url )
{
try
{
return loadProperties( url.openStream() );
}
catch ( Exception e )
{
// ignore
}
return null;
}
public static Properties loadProperties( File file )
{
try
{
return loadProperties( new FileInputStream( file ) );
}
catch ( Exception e )
{
// ignore
}
return null;
}
public static Properties loadProperties( InputStream is )
{
try
{
Properties properties = new Properties();
// Make sure the properties stream is valid
if ( is != null )
{
properties.load( is );
}
return properties;
}
catch ( IOException e )
{
// ignore
}
finally
{
try
{
if ( is != null )
{
is.close();
}
}
catch ( IOException e )
{
// ignore
}
}
return null;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/ReaderFactory.java 0000664 0000000 0000000 00000016424 11660305702 0027175 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.nio.charset.Charset;
import org.codehaus.plexus.util.xml.XmlStreamReader;
/**
* Utility to create Readers from streams, with explicit encoding choice: platform default,
* XML, or specified.
*
* @author Herve Boutemy
* @see Charset
* @see Supported encodings
* @version $Id: ReaderFactory.java 8010 2009-01-07 12:59:50Z vsiveton $
* @since 1.4.3
*/
public class ReaderFactory
{
/**
* ISO Latin Alphabet #1, also known as ISO-LATIN-1.
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String ISO_8859_1 = "ISO-8859-1";
/**
* Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set.
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String US_ASCII = "US-ASCII";
/**
* Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either
* order accepted on input, big-endian used on output).
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String UTF_16 = "UTF-16";
/**
* Sixteen-bit Unicode Transformation Format, big-endian byte order.
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String UTF_16BE = "UTF-16BE";
/**
* Sixteen-bit Unicode Transformation Format, little-endian byte order.
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String UTF_16LE = "UTF-16LE";
/**
* Eight-bit Unicode Transformation Format.
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String UTF_8 = "UTF-8";
/**
* The file.encoding
System Property.
*/
public static final String FILE_ENCODING = System.getProperty( "file.encoding" );
/**
* Create a new Reader with XML encoding detection rules.
*
* @param in not null input stream.
* @return an XML reader instance for the input stream.
* @throws IOException if any.
* @see XmlStreamReader
*/
public static XmlStreamReader newXmlReader( InputStream in )
throws IOException
{
return new XmlStreamReader( in );
}
/**
* Create a new Reader with XML encoding detection rules.
*
* @param file not null file.
* @return an XML reader instance for the input file.
* @throws IOException if any.
* @see XmlStreamReader
*/
public static XmlStreamReader newXmlReader( File file )
throws IOException
{
return new XmlStreamReader( file );
}
/**
* Create a new Reader with XML encoding detection rules.
*
* @param url not null url.
* @return an XML reader instance for the input url.
* @throws IOException if any.
* @see XmlStreamReader
*/
public static XmlStreamReader newXmlReader( URL url )
throws IOException
{
return new XmlStreamReader( url );
}
/**
* Create a new Reader with default plaform encoding.
*
* @param in not null input stream.
* @return a reader instance for the input stream using the default platform charset.
* @see Charset#defaultCharset()
*/
public static Reader newPlatformReader( InputStream in )
{
return new InputStreamReader( in );
}
/**
* Create a new Reader with default plaform encoding.
*
* @param file not null file.
* @return a reader instance for the input file using the default platform charset.
* @throws FileNotFoundException if any.
* @see Charset#defaultCharset()
*/
public static Reader newPlatformReader( File file )
throws FileNotFoundException
{
return new FileReader( file );
}
/**
* Create a new Reader with default plaform encoding.
*
* @param url not null url.
* @return a reader instance for the input url using the default platform charset.
* @throws IOException if any.
* @see Charset#defaultCharset()
*/
public static Reader newPlatformReader( URL url )
throws IOException
{
return new InputStreamReader( url.openStream() );
}
/**
* Create a new Reader with specified encoding.
*
* @param in not null input stream.
* @param encoding not null supported encoding.
* @return a reader instance for the input stream using the given encoding.
* @throws UnsupportedEncodingException if any.
* @see Supported encodings
*/
public static Reader newReader( InputStream in, String encoding )
throws UnsupportedEncodingException
{
return new InputStreamReader( in, encoding );
}
/**
* Create a new Reader with specified encoding.
*
* @param file not null file.
* @param encoding not null supported encoding.
* @return a reader instance for the input file using the given encoding.
* @throws FileNotFoundException if any.
* @throws UnsupportedEncodingException if any.
* @see Supported encodings
*/
public static Reader newReader( File file, String encoding )
throws FileNotFoundException, UnsupportedEncodingException
{
return new InputStreamReader( new FileInputStream(file), encoding );
}
/**
* Create a new Reader with specified encoding.
*
* @param url not null url.
* @param encoding not null supported encoding.
* @return a reader instance for the input url using the given encoding.
* @throws IOException if any.
* @see Supported encodings
*/
public static Reader newReader( URL url, String encoding )
throws IOException
{
return new InputStreamReader( url.openStream(), encoding );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/ReflectionUtils.java 0000664 0000000 0000000 00000016232 11660305702 0027553 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.AccessibleObject;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
/**
* @author Michal Maczka
* @author Jesse McConnell
* @author Trygve Laugstøl
* @version $Id: ReflectionUtils.java 8010 2009-01-07 12:59:50Z vsiveton $
*/
public final class ReflectionUtils
{
// ----------------------------------------------------------------------
// Field utils
// ----------------------------------------------------------------------
public static Field getFieldByNameIncludingSuperclasses( String fieldName, Class clazz )
{
Field retValue = null;
try
{
retValue = clazz.getDeclaredField( fieldName );
}
catch ( NoSuchFieldException e )
{
Class superclass = clazz.getSuperclass();
if ( superclass != null )
{
retValue = getFieldByNameIncludingSuperclasses( fieldName, superclass );
}
}
return retValue;
}
public static List getFieldsIncludingSuperclasses( Class clazz )
{
List fields = new ArrayList( Arrays.asList( clazz.getDeclaredFields() ) );
Class superclass = clazz.getSuperclass();
if ( superclass != null )
{
fields.addAll( getFieldsIncludingSuperclasses( superclass ) );
}
return fields;
}
// ----------------------------------------------------------------------
// Setter utils
// ----------------------------------------------------------------------
/**
* Finds a setter in the given class for the given field. It searches
* interfaces and superclasses too.
*
* @param fieldName the name of the field (i.e. 'fooBar'); it will search for a method named 'setFooBar'.
* @param clazz The class to find the method in.
* @return null or the method found.
*/
public static Method getSetter( String fieldName, Class clazz )
{
Method [] methods = clazz.getMethods();
fieldName = "set" + StringUtils.capitalizeFirstLetter( fieldName );
for ( int i = 0; i < methods.length; i++ )
{
Method method = methods[i];
if ( method.getName().equals( fieldName ) && isSetter( method ) )
{
return method;
}
}
return null;
}
/**
* Finds all setters in the given class and super classes.
*/
public static List getSetters( Class clazz )
{
Method[] methods = clazz.getMethods();
List list = new ArrayList();
for ( int i = 0; i < methods.length; i++ )
{
Method method = methods[i];
if ( isSetter( method ) )
{
list.add( method );
}
}
return list;
}
/**
* Returns the class of the argument to the setter.
*
* Will throw an RuntimeException if the method isn't a setter.
*/
public static Class getSetterType( Method method )
{
if ( !isSetter( method ) )
{
throw new RuntimeException( "The method " + method.getDeclaringClass().getName() + "." + method.getName() + " is not a setter." );
}
return method.getParameterTypes()[0];
}
// ----------------------------------------------------------------------
// Value accesstors
// ----------------------------------------------------------------------
/**
* attempts to set the value to the variable in the object passed in
*
* @param object
* @param variable
* @param value
* @throws IllegalAccessException
*/
public static void setVariableValueInObject( Object object, String variable, Object value )
throws IllegalAccessException
{
Field field = getFieldByNameIncludingSuperclasses( variable, object.getClass() );
field.setAccessible( true );
field.set(object, value );
}
/**
* Generates a map of the fields and values on a given object,
* also pulls from superclasses
*
* @param object the object to generate the list of fields from
* @return map containing the fields and their values
*/
public static Object getValueIncludingSuperclasses( String variable, Object object )
throws IllegalAccessException
{
Field field = getFieldByNameIncludingSuperclasses( variable, object.getClass() );
field.setAccessible( true );
return field.get( object );
}
/**
* Generates a map of the fields and values on a given object,
* also pulls from superclasses
*
* @param object the object to generate the list of fields from
* @return map containing the fields and their values
*/
public static Map getVariablesAndValuesIncludingSuperclasses( Object object )
throws IllegalAccessException
{
HashMap map = new HashMap ();
gatherVariablesAndValuesIncludingSuperclasses(object, map);
return map;
}
// ----------------------------------------------------------------------
// Private
// ----------------------------------------------------------------------
public static boolean isSetter( Method method )
{
return method.getReturnType().equals( Void.TYPE ) && // FIXME: needed /required?
!Modifier.isStatic( method.getModifiers() ) &&
method.getParameterTypes().length == 1;
}
/**
* populates a map of the fields and values on a given object,
* also pulls from superclasses
*
* @param object the object to generate the list of fields from
* @param map to populate
*/
private static void gatherVariablesAndValuesIncludingSuperclasses( Object object, Map map )
throws IllegalAccessException
{
Class clazz = object.getClass();
Field[] fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible( fields, true);
for (int i = 0; i < fields.length; ++i)
{
Field field = fields[i];
map.put( field.getName(), field.get( object ) );
}
Class superclass = clazz.getSuperclass();
if ( !Object.class.equals( superclass ) )
{
gatherVariablesAndValuesIncludingSuperclasses( superclass, map );
}
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/Scanner.java 0000664 0000000 0000000 00000006474 11660305702 0026040 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.File;
public interface Scanner
{
/**
* Sets the list of include patterns to use. All '/' and '\' characters
* are replaced by File.separatorChar
, so the separator used
* need not match File.separatorChar
.
*
* When a pattern ends with a '/' or '\', "**" is appended.
*
* @param includes A list of include patterns.
* May be null
, indicating that all files
* should be included. If a non-null
* list is given, all elements must be
* non-null
.
*/
void setIncludes(String[] includes);
/**
* Sets the list of exclude patterns to use. All '/' and '\' characters
* are replaced by File.separatorChar
, so the separator used
* need not match File.separatorChar
.
*
* When a pattern ends with a '/' or '\', "**" is appended.
*
* @param excludes A list of exclude patterns.
* May be null
, indicating that no files
* should be excluded. If a non-null
list is
* given, all elements must be non-null
.
*/
void setExcludes(String[] excludes);
/**
* Adds default exclusions to the current exclusions set.
*/
void addDefaultExcludes();
/**
* Scans the base directory for files which match at least one include
* pattern and don't match any exclude patterns.
*
* @exception IllegalStateException if the base directory was set
* incorrectly (i.e. if it is null
, doesn't exist,
* or isn't a directory).
*/
void scan();
/**
* Returns the names of the files which matched at least one of the
* include patterns and none of the exclude patterns.
* The names are relative to the base directory.
*
* @return the names of the files which matched at least one of the
* include patterns and none of the exclude patterns.
*/
String[] getIncludedFiles();
/**
* Returns the names of the directories which matched at least one of the
* include patterns and none of the exclude patterns.
* The names are relative to the base directory.
*
* @return the names of the directories which matched at least one of the
* include patterns and none of the exclude patterns.
*/
String[] getIncludedDirectories();
/**
* Returns the base directory to be scanned.
* This is the directory which is scanned recursively.
*
* @return the base directory to be scanned
*/
File getBasedir();
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/SelectorUtils.java 0000664 0000000 0000000 00000061423 11660305702 0027243 0 ustar 00root root 0000000 0000000 /*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.codehaus.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "Ant" and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact codehaus@codehaus.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*
This is a utility class used by selectors and DirectoryScanner. The * functionality more properly belongs just to selectors, but unfortunately * DirectoryScanner exposed these as protected methods. Thus we have to * support any subclasses of DirectoryScanner that may access these methods. *
*This is a Singleton.
* * @author Arnout J. Kuiper * ajkuiper@wxs.nl * @author Magesh Umasankar * @author Bruce Atherton * @since 1.5 * @version $Id: SelectorUtils.java 8482 2009-10-18 19:09:52Z bentmann $ */ public final class SelectorUtils { public static final String PATTERN_HANDLER_PREFIX = "["; public static final String PATTERN_HANDLER_SUFFIX = "]"; public static final String REGEX_HANDLER_PREFIX = "%regex" + PATTERN_HANDLER_PREFIX; public static final String ANT_HANDLER_PREFIX = "%ant" + PATTERN_HANDLER_PREFIX; private static SelectorUtils instance = new SelectorUtils(); /** * Private Constructor */ private SelectorUtils() { } /** * Retrieves the manager of the Singleton. */ public static SelectorUtils getInstance() { return instance; } /** * Tests whether or not a given path matches the start of a given * pattern up to the first "**". *
* This is not a general purpose test and should only be used if you
* can live with false positives. For example, pattern=**\a
* and str=b
will yield true
.
*
* @param pattern The pattern to match against. Must not be
* null
.
* @param str The path to match, as a String. Must not be
* null
.
*
* @return whether or not a given path matches the start of a given
* pattern up to the first "**".
*/
public static boolean matchPatternStart( String pattern, String str )
{
return matchPatternStart( pattern, str, true );
}
/**
* Tests whether or not a given path matches the start of a given
* pattern up to the first "**".
*
* This is not a general purpose test and should only be used if you
* can live with false positives. For example, pattern=**\a
* and str=b
will yield true
.
*
* @param pattern The pattern to match against. Must not be
* null
.
* @param str The path to match, as a String. Must not be
* null
.
* @param isCaseSensitive Whether or not matching should be performed
* case sensitively.
*
* @return whether or not a given path matches the start of a given
* pattern up to the first "**".
*/
public static boolean matchPatternStart( String pattern, String str,
boolean isCaseSensitive )
{
if ( pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
&& pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) )
{
// FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have
// a file to deal with, or we can definitely say this is an exclusion...
return true;
}
else
{
if ( pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
&& pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) )
{
pattern =
pattern.substring( ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
}
String altStr = str.replace( '\\', '/' );
return matchAntPathPatternStart( pattern, str, File.separator, isCaseSensitive )
|| matchAntPathPatternStart( pattern, altStr, "/", isCaseSensitive );
}
}
private static boolean matchAntPathPatternStart( String pattern, String str, String separator, boolean isCaseSensitive )
{
// When str starts with a File.separator, pattern has to start with a
// File.separator.
// When pattern starts with a File.separator, str has to start with a
// File.separator.
if ( str.startsWith( separator ) !=
pattern.startsWith( separator ) )
{
return false;
}
Vector patDirs = tokenizePath( pattern, separator );
Vector strDirs = tokenizePath( str, separator );
int patIdxStart = 0;
int patIdxEnd = patDirs.size() - 1;
int strIdxStart = 0;
int strIdxEnd = strDirs.size() - 1;
// up to first '**'
while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
{
String patDir = (String) patDirs.elementAt( patIdxStart );
if ( patDir.equals( "**" ) )
{
break;
}
if ( !match( patDir, (String) strDirs.elementAt( strIdxStart ),
isCaseSensitive ) )
{
return false;
}
patIdxStart++;
strIdxStart++;
}
if ( strIdxStart > strIdxEnd )
{
// String is exhausted
return true;
}
else if ( patIdxStart > patIdxEnd )
{
// String not exhausted, but pattern is. Failure.
return false;
}
else
{
// pattern now holds ** while string is not exhausted
// this will generate false positives but we can live with that.
return true;
}
}
/**
* Tests whether or not a given path matches a given pattern.
*
* @param pattern The pattern to match against. Must not be
* null
.
* @param str The path to match, as a String. Must not be
* null
.
*
* @return true
if the pattern matches against the string,
* or false
otherwise.
*/
public static boolean matchPath( String pattern, String str )
{
return matchPath( pattern, str, true );
}
/**
* Tests whether or not a given path matches a given pattern.
*
* @param pattern The pattern to match against. Must not be
* null
.
* @param str The path to match, as a String. Must not be
* null
.
* @param isCaseSensitive Whether or not matching should be performed
* case sensitively.
*
* @return true
if the pattern matches against the string,
* or false
otherwise.
*/
public static boolean matchPath( String pattern, String str,
boolean isCaseSensitive )
{
if ( pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
&& pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) )
{
pattern = pattern.substring( REGEX_HANDLER_PREFIX.length(), pattern.length()
- PATTERN_HANDLER_SUFFIX.length() );
return str.matches( pattern );
}
else
{
if ( pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
&& pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) )
{
pattern =
pattern.substring( ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
}
return matchAntPathPattern( pattern, str, isCaseSensitive );
}
}
private static boolean matchAntPathPattern( String pattern, String str, boolean isCaseSensitive )
{
// When str starts with a File.separator, pattern has to start with a
// File.separator.
// When pattern starts with a File.separator, str has to start with a
// File.separator.
if ( str.startsWith( File.separator ) !=
pattern.startsWith( File.separator ) )
{
return false;
}
Vector patDirs = tokenizePath( pattern, File.separator );
Vector strDirs = tokenizePath( str, File.separator );
int patIdxStart = 0;
int patIdxEnd = patDirs.size() - 1;
int strIdxStart = 0;
int strIdxEnd = strDirs.size() - 1;
// up to first '**'
while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
{
String patDir = (String) patDirs.elementAt( patIdxStart );
if ( patDir.equals( "**" ) )
{
break;
}
if ( !match( patDir, (String) strDirs.elementAt( strIdxStart ),
isCaseSensitive ) )
{
patDirs = null;
strDirs = null;
return false;
}
patIdxStart++;
strIdxStart++;
}
if ( strIdxStart > strIdxEnd )
{
// String is exhausted
for ( int i = patIdxStart; i <= patIdxEnd; i++ )
{
if ( !patDirs.elementAt( i ).equals( "**" ) )
{
patDirs = null;
strDirs = null;
return false;
}
}
return true;
}
else
{
if ( patIdxStart > patIdxEnd )
{
// String not exhausted, but pattern is. Failure.
patDirs = null;
strDirs = null;
return false;
}
}
// up to last '**'
while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
{
String patDir = (String) patDirs.elementAt( patIdxEnd );
if ( patDir.equals( "**" ) )
{
break;
}
if ( !match( patDir, (String) strDirs.elementAt( strIdxEnd ),
isCaseSensitive ) )
{
patDirs = null;
strDirs = null;
return false;
}
patIdxEnd--;
strIdxEnd--;
}
if ( strIdxStart > strIdxEnd )
{
// String is exhausted
for ( int i = patIdxStart; i <= patIdxEnd; i++ )
{
if ( !patDirs.elementAt( i ).equals( "**" ) )
{
patDirs = null;
strDirs = null;
return false;
}
}
return true;
}
while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
{
int patIdxTmp = -1;
for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
{
if ( patDirs.elementAt( i ).equals( "**" ) )
{
patIdxTmp = i;
break;
}
}
if ( patIdxTmp == patIdxStart + 1 )
{
// '**/**' situation, so skip one
patIdxStart++;
continue;
}
// Find the pattern between padIdxStart & padIdxTmp in str between
// strIdxStart & strIdxEnd
int patLength = ( patIdxTmp - patIdxStart - 1 );
int strLength = ( strIdxEnd - strIdxStart + 1 );
int foundIdx = -1;
strLoop:
for ( int i = 0; i <= strLength - patLength; i++ )
{
for ( int j = 0; j < patLength; j++ )
{
String subPat = (String) patDirs.elementAt( patIdxStart + j + 1 );
String subStr = (String) strDirs.elementAt( strIdxStart + i + j );
if ( !match( subPat, subStr, isCaseSensitive ) )
{
continue strLoop;
}
}
foundIdx = strIdxStart + i;
break;
}
if ( foundIdx == -1 )
{
patDirs = null;
strDirs = null;
return false;
}
patIdxStart = patIdxTmp;
strIdxStart = foundIdx + patLength;
}
for ( int i = patIdxStart; i <= patIdxEnd; i++ )
{
if ( !patDirs.elementAt( i ).equals( "**" ) )
{
patDirs = null;
strDirs = null;
return false;
}
}
return true;
}
/**
* Tests whether or not a string matches against a pattern.
* The pattern may contain two special characters:
* '*' means zero or more characters
* '?' means one and only one character
*
* @param pattern The pattern to match against.
* Must not be null
.
* @param str The string which must be matched against the pattern.
* Must not be null
.
*
* @return true
if the string matches against the pattern,
* or false
otherwise.
*/
public static boolean match( String pattern, String str )
{
return match( pattern, str, true );
}
/**
* Tests whether or not a string matches against a pattern.
* The pattern may contain two special characters:
* '*' means zero or more characters
* '?' means one and only one character
*
* @param pattern The pattern to match against.
* Must not be null
.
* @param str The string which must be matched against the pattern.
* Must not be null
.
* @param isCaseSensitive Whether or not matching should be performed
* case sensitively.
*
*
* @return true
if the string matches against the pattern,
* or false
otherwise.
*/
public static boolean match( String pattern, String str,
boolean isCaseSensitive )
{
char[] patArr = pattern.toCharArray();
char[] strArr = str.toCharArray();
int patIdxStart = 0;
int patIdxEnd = patArr.length - 1;
int strIdxStart = 0;
int strIdxEnd = strArr.length - 1;
char ch;
boolean containsStar = false;
for ( int i = 0; i < patArr.length; i++ )
{
if ( patArr[i] == '*' )
{
containsStar = true;
break;
}
}
if ( !containsStar )
{
// No '*'s, so we make a shortcut
if ( patIdxEnd != strIdxEnd )
{
return false; // Pattern and string do not have the same size
}
for ( int i = 0; i <= patIdxEnd; i++ )
{
ch = patArr[i];
if ( ch != '?' && !equals( ch, strArr[i], isCaseSensitive ) )
{
return false; // Character mismatch
}
}
return true; // String matches against pattern
}
if ( patIdxEnd == 0 )
{
return true; // Pattern contains only '*', which matches anything
}
// Process characters before first star
while ( ( ch = patArr[patIdxStart] ) != '*' && strIdxStart <= strIdxEnd )
{
if ( ch != '?' && !equals( ch, strArr[strIdxStart], isCaseSensitive ) )
{
return false; // Character mismatch
}
patIdxStart++;
strIdxStart++;
}
if ( strIdxStart > strIdxEnd )
{
// All characters in the string are used. Check if only '*'s are
// left in the pattern. If so, we succeeded. Otherwise failure.
for ( int i = patIdxStart; i <= patIdxEnd; i++ )
{
if ( patArr[i] != '*' )
{
return false;
}
}
return true;
}
// Process characters after last star
while ( ( ch = patArr[patIdxEnd] ) != '*' && strIdxStart <= strIdxEnd )
{
if ( ch != '?' && !equals( ch, strArr[strIdxEnd], isCaseSensitive ) )
{
return false; // Character mismatch
}
patIdxEnd--;
strIdxEnd--;
}
if ( strIdxStart > strIdxEnd )
{
// All characters in the string are used. Check if only '*'s are
// left in the pattern. If so, we succeeded. Otherwise failure.
for ( int i = patIdxStart; i <= patIdxEnd; i++ )
{
if ( patArr[i] != '*' )
{
return false;
}
}
return true;
}
// process pattern between stars. padIdxStart and patIdxEnd point
// always to a '*'.
while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
{
int patIdxTmp = -1;
for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
{
if ( patArr[i] == '*' )
{
patIdxTmp = i;
break;
}
}
if ( patIdxTmp == patIdxStart + 1 )
{
// Two stars next to each other, skip the first one.
patIdxStart++;
continue;
}
// Find the pattern between padIdxStart & padIdxTmp in str between
// strIdxStart & strIdxEnd
int patLength = ( patIdxTmp - patIdxStart - 1 );
int strLength = ( strIdxEnd - strIdxStart + 1 );
int foundIdx = -1;
strLoop:
for ( int i = 0; i <= strLength - patLength; i++ )
{
for ( int j = 0; j < patLength; j++ )
{
ch = patArr[patIdxStart + j + 1];
if ( ch != '?' && !equals( ch, strArr[strIdxStart + i + j], isCaseSensitive ) )
{
continue strLoop;
}
}
foundIdx = strIdxStart + i;
break;
}
if ( foundIdx == -1 )
{
return false;
}
patIdxStart = patIdxTmp;
strIdxStart = foundIdx + patLength;
}
// All characters in the string are used. Check if only '*'s are left
// in the pattern. If so, we succeeded. Otherwise failure.
for ( int i = patIdxStart; i <= patIdxEnd; i++ )
{
if ( patArr[i] != '*' )
{
return false;
}
}
return true;
}
/**
* Tests whether two characters are equal.
*/
private static boolean equals( char c1, char c2, boolean isCaseSensitive )
{
if ( c1 == c2 )
{
return true;
}
if ( !isCaseSensitive )
{
// NOTE: Try both upper case and lower case as done by String.equalsIgnoreCase()
if ( Character.toUpperCase( c1 ) == Character.toUpperCase( c2 ) ||
Character.toLowerCase( c1 ) == Character.toLowerCase( c2 ) )
{
return true;
}
}
return false;
}
/**
* Breaks a path up into a Vector of path elements, tokenizing on
* File.separator
.
*
* @param path Path to tokenize. Must not be null
.
*
* @return a Vector of path elements from the tokenized path
*/
public static Vector tokenizePath( String path )
{
return tokenizePath( path, File.separator );
}
public static Vector tokenizePath( String path, String separator )
{
Vector ret = new Vector();
StringTokenizer st = new StringTokenizer( path, separator );
while ( st.hasMoreTokens() )
{
ret.addElement( st.nextToken() );
}
return ret;
}
/**
* Returns dependency information on these two files. If src has been
* modified later than target, it returns true. If target doesn't exist,
* it likewise returns true. Otherwise, target is newer than src and
* is not out of date, thus the method returns false. It also returns
* false if the src file doesn't even exist, since how could the
* target then be out of date.
*
* @param src the original file
* @param target the file being compared against
* @param granularity the amount in seconds of slack we will give in
* determining out of dateness
* @return whether the target is out of date
*/
public static boolean isOutOfDate( File src, File target, int granularity )
{
if ( !src.exists() )
{
return false;
}
if ( !target.exists() )
{
return true;
}
if ( ( src.lastModified() - granularity ) > target.lastModified() )
{
return true;
}
return false;
}
/**
* "Flattens" a string by removing all whitespace (space, tab, linefeed,
* carriage return, and formfeed). This uses StringTokenizer and the
* default set of tokens as documented in the single arguement constructor.
*
* @param input a String to remove all whitespace.
* @return a String that has had all whitespace removed.
*/
public static String removeWhitespace( String input )
{
StringBuffer result = new StringBuffer();
if ( input != null )
{
StringTokenizer st = new StringTokenizer( input );
while ( st.hasMoreTokens() )
{
result.append( st.nextToken() );
}
}
return result.toString();
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/StringInputStream.java 0000664 0000000 0000000 00000012166 11660305702 0030104 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.codehaus.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache MavenSession" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact codehaus@codehaus.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache MavenSession", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* null
.
*/
public StringInputStream( String source )
{
in = new StringReader( source );
}
/**
* Reads from the Stringreader, returning the same value. Note that
* data will be lost for characters not in ISO Latin 1. Clients
* assuming a return value in the range -1 to 255 may even fail on
* such input.
*
* @return the value of the next character in the StringReader
*
* @exception IOException if the original StringReader fails to be read
*/
public int read() throws IOException
{
return in.read();
}
/**
* Closes the Stringreader.
*
* @exception IOException if the original StringReader fails to be closed
*/
public void close() throws IOException
{
in.close();
}
/**
* Marks the read limit of the StringReader.
*
* @param limit the maximum limit of bytes that can be read before the
* mark position becomes invalid
*/
public synchronized void mark( final int limit )
{
try
{
in.mark( limit );
}
catch ( IOException ioe )
{
throw new RuntimeException( ioe.getMessage() );
}
}
/**
* Resets the StringReader.
*
* @exception IOException if the StringReader fails to be reset
*/
public synchronized void reset() throws IOException
{
in.reset();
}
/**
* @see InputStream#markSupported
*/
public boolean markSupported()
{
return in.markSupported();
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/StringOutputStream.java 0000664 0000000 0000000 00000003243 11660305702 0030301 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.IOException;
import java.io.OutputStream;
/**
* Wraps a String as an OutputStream.
*
* @author Emmanuel Venisse
* @version $Id: StringOutputStream.java 8010 2009-01-07 12:59:50Z vsiveton $
* @deprecated As of version 1.5.2 this class should no longer be used because it does not properly handle character
* encoding. Instead, use {@link java.io.ByteArrayOutputStream#toString(String)}.
*/
public class StringOutputStream
extends OutputStream
{
private StringBuffer buf = new StringBuffer();
public void write( byte[] b ) throws IOException
{
buf.append( new String( b ) );
}
public void write( byte[] b, int off, int len ) throws IOException
{
buf.append( new String( b, off, len ) );
}
public void write( int b ) throws IOException
{
byte[] bytes = new byte[1];
bytes[0] = (byte)b;
buf.append( new String( bytes ) );
}
public String toString()
{
return buf.toString();
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/StringUtils.java 0000664 0000000 0000000 00000225003 11660305702 0026725 0 ustar 00root root 0000000 0000000 /* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.codehaus.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact codehaus@codehaus.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*
Common String
manipulation routines.
Originally from * Turbine and the * GenerationJavaCore library.
* * @author Jon S. Stevens * @author Daniel Rall * @author Greg Coladonato * @author Henri Yandell * @author Ed Korthof * @author Rand McNeely * @author Stephen Colebourne * @author Fredrik Westermarck * @author Holger Krauth * @author Alexander Day Chaffee * @author Vincent Siveton * @since 1.0 * @version $Id: StringUtils.java 8001 2009-01-03 13:17:09Z vsiveton $ */ public class StringUtils { /** *StringUtils
instances should NOT be constructed in
* standard programming. Instead, the class should be used as
* StringUtils.trim(" foo ");
.
This constructor is public to permit tools that require a JavaBean * manager to operate.
*/ public StringUtils() { } // Empty //-------------------------------------------------------------------------- /** *Removes control characters, including whitespace, from both
* ends of this String, handling null
by returning
* an empty String.
null
)
*/
public static String clean( String str )
{
return ( str == null ? "" : str.trim() );
}
/**
* Removes control characters, including whitespace, from both
* ends of this String, handling null
by returning
* null
.
null
)
*/
public static String trim( String str )
{
return ( str == null ? null : str.trim() );
}
/**
* Deletes all whitespaces from a String.
* *Whitespace is defined by * {@link Character#isWhitespace(char)}.
* * @param str String target to delete whitespace from * @return the String without whitespaces * @throws NullPointerException */ public static String deleteWhitespace( String str ) { StringBuffer buffer = new StringBuffer(); int sz = str.length(); for ( int i = 0; i < sz; i++ ) { if ( !Character.isWhitespace( str.charAt( i ) ) ) { buffer.append( str.charAt( i ) ); } } return buffer.toString(); } /** *Checks if a String is non null
and is
* not empty (length > 0
).
Checks if a (trimmed) String is null
or empty.
Note: In future releases, this method will no longer trim the input string such that it works * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be * migrated to use {@link #isBlank(String)} instead.
* * @param str the String to check * @returntrue
if the String is null
, or
* length zero once trimmed
*/
public static boolean isEmpty( String str )
{
return ( ( str == null ) || ( str.trim().length() == 0 ) );
}
/**
* * Checks if a String is whitespace, empty ("") or null. *
* ** StringUtils.isBlank(null) = true * StringUtils.isBlank("") = true * StringUtils.isBlank(" ") = true * StringUtils.isBlank("bob") = false * StringUtils.isBlank(" bob ") = false ** * @param str the String to check, may be null * @return
true
if the String is null, empty or whitespace
* @since 1.5.2
*/
public static boolean isBlank( String str )
{
int strLen;
if ( str == null || ( strLen = str.length() ) == 0 )
{
return true;
}
for ( int i = 0; i < strLen; i++ )
{
if ( !Character.isWhitespace( str.charAt( i ) ) )
{
return false;
}
}
return true;
}
/**
* * Checks if a String is not empty (""), not null and not whitespace only. *
* ** StringUtils.isNotBlank(null) = false * StringUtils.isNotBlank("") = false * StringUtils.isNotBlank(" ") = false * StringUtils.isNotBlank("bob") = true * StringUtils.isNotBlank(" bob ") = true ** * @param str the String to check, may be null * @return
true
if the String is not empty and not null and not whitespace
* @since 1.5.2
*/
public static boolean isNotBlank( String str )
{
return !StringUtils.isBlank( str );
}
// Equals and IndexOf
//--------------------------------------------------------------------------
/**
* Compares two Strings, returning true
if they are equal.
null
s are handled without exceptions. Two null
* references are considered to be equal. The comparison is case sensitive.
true
if the Strings are equal, case sensitive, or
* both null
*/
public static boolean equals( String str1, String str2 )
{
return ( str1 == null ? str2 == null : str1.equals( str2 ) );
}
/**
* Compares two Strings, returning true
if they are equal ignoring
* the case.
Nulls
are handled without exceptions. Two null
* references are considered equal. Comparison is case insensitive.
true
if the Strings are equal, case insensitive, or
* both null
*/
public static boolean equalsIgnoreCase( String str1, String str2 )
{
return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) );
}
/**
* Find the first index of any of a set of potential substrings.
* *null
String will return -1
.
null
*/
public static int indexOfAny( String str, String[] searchStrs )
{
if ( ( str == null ) || ( searchStrs == null ) )
{
return -1;
}
int sz = searchStrs.length;
// String's can't have a MAX_VALUEth index.
int ret = Integer.MAX_VALUE;
int tmp = 0;
for ( int i = 0; i < sz; i++ )
{
tmp = str.indexOf( searchStrs[i] );
if ( tmp == -1 )
{
continue;
}
if ( tmp < ret )
{
ret = tmp;
}
}
return ( ret == Integer.MAX_VALUE ) ? -1 : ret;
}
/**
* Find the latest index of any of a set of potential substrings.
* *null
string will return -1
.
null
*/
public static int lastIndexOfAny( String str, String[] searchStrs )
{
if ( ( str == null ) || ( searchStrs == null ) )
{
return -1;
}
int sz = searchStrs.length;
int ret = -1;
int tmp = 0;
for ( int i = 0; i < sz; i++ )
{
tmp = str.lastIndexOf( searchStrs[i] );
if ( tmp > ret )
{
ret = tmp;
}
}
return ret;
}
// Substring
//--------------------------------------------------------------------------
/**
* Gets a substring from the specified string avoiding exceptions.
* *A negative start position can be used to start n
* characters from the end of the String.
Gets a substring from the specified String avoiding exceptions.
* *A negative start position can be used to start/end n
* characters from the end of the String.
Gets the leftmost n
characters of a String.
If n
characters are not available, or the
* String is null
, the String will be returned without
* an exception.
Gets the rightmost n
characters of a String.
If n
characters are not available, or the String
* is null
, the String will be returned without an
* exception.
Gets n
characters from the middle of a String.
If n
characters are not available, the remainder
* of the String will be returned without an exception. If the
* String is null
, null
will be returned.
Splits the provided text into a array, using whitespace as the * separator.
* *The separator is not included in the returned String array.
* * @param str the String to parse * @return an array of parsed Strings */ public static String[] split( String str ) { return split( str, null, -1 ); } /** * @see #split(String, String, int) */ public static String[] split( String text, String separator ) { return split( text, separator, -1 ); } /** *Splits the provided text into a array, based on a given separator.
* *The separator is not included in the returned String array. The
* maximum number of splits to perfom can be controlled. A null
* separator will cause parsing to be on whitespace.
This is useful for quickly splitting a String directly into
* an array of tokens, instead of an enumeration of tokens (as
* StringTokenizer
does).
null
, splits on whitespace.
* @param max The maximum number of elements to include in the
* array. A zero or negative value implies no limit.
* @return an array of parsed Strings
*/
public static String[] split( String str, String separator, int max )
{
StringTokenizer tok = null;
if ( separator == null )
{
// Null separator means we're using StringTokenizer's default
// delimiter, which comprises all whitespace characters.
tok = new StringTokenizer( str );
}
else
{
tok = new StringTokenizer( str, separator );
}
int listSize = tok.countTokens();
if ( ( max > 0 ) && ( listSize > max ) )
{
listSize = max;
}
String[] list = new String[listSize];
int i = 0;
int lastTokenBegin = 0;
int lastTokenEnd = 0;
while ( tok.hasMoreTokens() )
{
if ( ( max > 0 ) && ( i == listSize - 1 ) )
{
// In the situation where we hit the max yet have
// tokens left over in our input, the last list
// element gets all remaining text.
String endToken = tok.nextToken();
lastTokenBegin = str.indexOf( endToken, lastTokenEnd );
list[i] = str.substring( lastTokenBegin );
break;
}
else
{
list[i] = tok.nextToken();
lastTokenBegin = str.indexOf( list[i], lastTokenEnd );
lastTokenEnd = lastTokenBegin + list[i].length();
}
i++;
}
return list;
}
// Joining
//--------------------------------------------------------------------------
/**
* Concatenates elements of an array into a single String.
* *The difference from join is that concatenate has no delimiter.
* * @param array the array of values to concatenate. * @return the concatenated string. */ public static String concatenate( Object[] array ) { return join( array, "" ); } /** *Joins the elements of the provided array into a single String * containing the provided list of elements.
* *No delimiter is added before or after the list. A
* null
separator is the same as a blank String.
Joins the elements of the provided Iterator
into
* a single String containing the provided elements.
No delimiter is added before or after the list. A
* null
separator is the same as a blank String.
Iterator
of values to join together
* @param separator the separator character to use
* @return the joined String
*/
public static String join( Iterator iterator, String separator )
{
if ( separator == null )
{
separator = "";
}
StringBuffer buf = new StringBuffer( 256 ); // Java default is 16, probably too small
while ( iterator.hasNext() )
{
buf.append( iterator.next() );
if ( iterator.hasNext() )
{
buf.append( separator );
}
}
return buf.toString();
}
// Replacing
//--------------------------------------------------------------------------
/**
* Replace a char with another char inside a larger String, once.
* *A null
reference passed to this method is a no-op.
Replace all occurances of a char within another char.
* *A null
reference passed to this method is a no-op.
Replace a char with another char inside a larger String,
* for the first max
values of the search char.
A null
reference passed to this method is a no-op.
-1
if no maximum
* @return the text with any replacements processed
*/
public static String replace( String text, char repl, char with, int max )
{
return replace( text, String.valueOf( repl ), String.valueOf( with ), max );
}
/**
* Replace a String with another String inside a larger String, once.
* *A null
reference passed to this method is a no-op.
Replace all occurances of a String within another String.
* *A null
reference passed to this method is a no-op.
Replace a String with another String inside a larger String,
* for the first max
values of the search String.
A null
reference passed to this method is a no-op.
-1
if no maximum
* @return the text with any replacements processed
*/
public static String replace( String text, String repl, String with, int max )
{
if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) )
{
return text;
}
StringBuffer buf = new StringBuffer( text.length() );
int start = 0, end = 0;
while ( ( end = text.indexOf( repl, start ) ) != -1 )
{
buf.append( text.substring( start, end ) ).append( with );
start = end + repl.length();
if ( --max == 0 )
{
break;
}
}
buf.append( text.substring( start ) );
return buf.toString();
}
/**
* Overlay a part of a String with another String.
* * @param text String to do overlaying in * @param overlay String to overlay * @param start int to start overlaying at * @param end int to stop overlaying before * @return String with overlayed text * @throws NullPointerException if text or overlay isnull
*/
public static String overlayString( String text, String overlay, int start, int end )
{
return new StringBuffer( start + overlay.length() + text.length() - end + 1 )
.append( text.substring( 0, start ) )
.append( overlay )
.append( text.substring( end ) )
.toString();
}
// Centering
//--------------------------------------------------------------------------
/**
* Center a String in a larger String of size n
.
* *
Uses spaces as the value to buffer the String with.
* Equivalent to center(str, size, " ")
.
null
*/
public static String center( String str, int size )
{
return center( str, size, " " );
}
/**
* Center a String in a larger String of size n
.
Uses a supplied String as the value to buffer the String with.
* * @param str String to center * @param size int size of new String * @param delim String to buffer the new String with * @return String containing centered String * @throws NullPointerException if str or delim isnull
* @throws ArithmeticException if delim is the empty String
*/
public static String center( String str, int size, String delim )
{
int sz = str.length();
int p = size - sz;
if ( p < 1 )
{
return str;
}
str = leftPad( str, sz + p / 2, delim );
str = rightPad( str, size, delim );
return str;
}
// Chomping
//--------------------------------------------------------------------------
/**
* Remove the last newline, and everything after it from a String.
* * @param str String to chomp the newline from * @return String without chomped newline * @throws NullPointerException if str isnull
*/
public static String chomp( String str )
{
return chomp( str, "\n" );
}
/**
* Remove the last value of a supplied String, and everything after * it from a String.
* * @param str String to chomp from * @param sep String to chomp * @return String without chomped ending * @throws NullPointerException if str or sep isnull
*/
public static String chomp( String str, String sep )
{
int idx = str.lastIndexOf( sep );
if ( idx != -1 )
{
return str.substring( 0, idx );
}
else
{
return str;
}
}
/**
* Remove a newline if and only if it is at the end * of the supplied String.
* * @param str String to chomp from * @return String without chomped ending * @throws NullPointerException if str isnull
*/
public static String chompLast( String str )
{
return chompLast( str, "\n" );
}
/**
* Remove a value if and only if the String ends with that value.
* * @param str String to chomp from * @param sep String to chomp * @return String without chomped ending * @throws NullPointerException if str or sep isnull
*/
public static String chompLast( String str, String sep )
{
if ( str.length() == 0 )
{
return str;
}
String sub = str.substring( str.length() - sep.length() );
if ( sep.equals( sub ) )
{
return str.substring( 0, str.length() - sep.length() );
}
else
{
return str;
}
}
/**
* Remove everything and return the last value of a supplied String, and * everything after it from a String.
* * @param str String to chomp from * @param sep String to chomp * @return String chomped * @throws NullPointerException if str or sep isnull
*/
public static String getChomp( String str, String sep )
{
int idx = str.lastIndexOf( sep );
if ( idx == str.length() - sep.length() )
{
return sep;
}
else if ( idx != -1 )
{
return str.substring( idx );
}
else
{
return "";
}
}
/**
* Remove the first value of a supplied String, and everything before it * from a String.
* * @param str String to chomp from * @param sep String to chomp * @return String without chomped beginning * @throws NullPointerException if str or sep isnull
*/
public static String prechomp( String str, String sep )
{
int idx = str.indexOf( sep );
if ( idx != -1 )
{
return str.substring( idx + sep.length() );
}
else
{
return str;
}
}
/**
* Remove and return everything before the first value of a * supplied String from another String.
* * @param str String to chomp from * @param sep String to chomp * @return String prechomped * @throws NullPointerException if str or sep isnull
*/
public static String getPrechomp( String str, String sep )
{
int idx = str.indexOf( sep );
if ( idx != -1 )
{
return str.substring( 0, idx + sep.length() );
}
else
{
return "";
}
}
// Chopping
//--------------------------------------------------------------------------
/**
* Remove the last character from a String.
* *If the String ends in \r\n
, then remove both
* of them.
null
*/
public static String chop( String str )
{
if ( "".equals( str ) )
{
return "";
}
if ( str.length() == 1 )
{
return "";
}
int lastIdx = str.length() - 1;
String ret = str.substring( 0, lastIdx );
char last = str.charAt( lastIdx );
if ( last == '\n' )
{
if ( ret.charAt( lastIdx - 1 ) == '\r' )
{
return ret.substring( 0, lastIdx - 1 );
}
}
return ret;
}
/**
* Remove \n
from end of a String if it's there.
* If a \r
precedes it, then remove that too.
null
*/
public static String chopNewline( String str )
{
int lastIdx = str.length() - 1;
char last = str.charAt( lastIdx );
if ( last == '\n' )
{
if ( str.charAt( lastIdx - 1 ) == '\r' )
{
lastIdx--;
}
}
else
{
lastIdx++;
}
return str.substring( 0, lastIdx );
}
// Conversion
//--------------------------------------------------------------------------
// spec 3.10.6
/**
* Escapes any values it finds into their String form.
* *So a tab becomes the characters '\\'
and
* 't'
.
null
*/
public static String escape( String str )
{
// improved with code from cybertiger@cyberiantiger.org
// unicode from him, and defaul for < 32's.
int sz = str.length();
StringBuffer buffer = new StringBuffer( 2 * sz );
for ( int i = 0; i < sz; i++ )
{
char ch = str.charAt( i );
// handle unicode
if ( ch > 0xfff )
{
buffer.append( "\\u" + Integer.toHexString( ch ) );
}
else if ( ch > 0xff )
{
buffer.append( "\\u0" + Integer.toHexString( ch ) );
}
else if ( ch > 0x7f )
{
buffer.append( "\\u00" + Integer.toHexString( ch ) );
}
else if ( ch < 32 )
{
switch ( ch )
{
case '\b':
buffer.append( '\\' );
buffer.append( 'b' );
break;
case '\n':
buffer.append( '\\' );
buffer.append( 'n' );
break;
case '\t':
buffer.append( '\\' );
buffer.append( 't' );
break;
case '\f':
buffer.append( '\\' );
buffer.append( 'f' );
break;
case '\r':
buffer.append( '\\' );
buffer.append( 'r' );
break;
default :
if ( ch > 0xf )
{
buffer.append( "\\u00" + Integer.toHexString( ch ) );
}
else
{
buffer.append( "\\u000" + Integer.toHexString( ch ) );
}
break;
}
}
else
{
switch ( ch )
{
case '\'':
buffer.append( '\\' );
buffer.append( '\'' );
break;
case '"':
buffer.append( '\\' );
buffer.append( '"' );
break;
case '\\':
buffer.append( '\\' );
buffer.append( '\\' );
break;
default :
buffer.append( ch );
break;
}
}
}
return buffer.toString();
}
// Padding
//--------------------------------------------------------------------------
/**
* Repeat a String n
times to form a
* new string.
repeat < 0
* @throws NullPointerException if str is null
*/
public static String repeat( String str, int repeat )
{
StringBuffer buffer = new StringBuffer( repeat * str.length() );
for ( int i = 0; i < repeat; i++ )
{
buffer.append( str );
}
return buffer.toString();
}
/**
* Right pad a String with spaces.
* *The String is padded to the size of n
.
null
*/
public static String rightPad( String str, int size )
{
return rightPad( str, size, " " );
}
/**
* Right pad a String with a specified string.
* *The String is padded to the size of n
.
null
* @throws ArithmeticException if delim is the empty String
*/
public static String rightPad( String str, int size, String delim )
{
size = ( size - str.length() ) / delim.length();
if ( size > 0 )
{
str += repeat( delim, size );
}
return str;
}
/**
* Left pad a String with spaces.
* *The String is padded to the size of n
.
null
*/
public static String leftPad( String str, int size )
{
return leftPad( str, size, " " );
}
/**
* Left pad a String with a specified string. Pad to a size of n.
*
* @param str String to pad out
* @param size size to pad to
* @param delim String to pad with
* @return left padded String
* @throws NullPointerException if str or delim is null
* @throws ArithmeticException if delim is the empty string
*/
public static String leftPad( String str, int size, String delim )
{
size = ( size - str.length() ) / delim.length();
if ( size > 0 )
{
str = repeat( delim, size ) + str;
}
return str;
}
// Stripping
//--------------------------------------------------------------------------
/**
* Remove whitespace from the front and back of a String.
* * @param str the String to remove whitespace from * @return the stripped String */ public static String strip( String str ) { return strip( str, null ); } /** *Remove a specified String from the front and back of a * String.
* *If whitespace is wanted to be removed, used the * {@link #strip(java.lang.String)} method.
* * @param str the String to remove a string from * @param delim the String to remove at start and end * @return the stripped String */ public static String strip( String str, String delim ) { str = stripStart( str, delim ); return stripEnd( str, delim ); } /** *Strip whitespace from the front and back of every String * in the array.
* * @param strs the Strings to remove whitespace from * @return the stripped Strings */ public static String[] stripAll( String[] strs ) { return stripAll( strs, null ); } /** *Strip the specified delimiter from the front and back of * every String in the array.
* * @param strs the Strings to remove a String from * @param delimiter the String to remove at start and end * @return the stripped Strings */ public static String[] stripAll( String[] strs, String delimiter ) { if ( ( strs == null ) || ( strs.length == 0 ) ) { return strs; } int sz = strs.length; String[] newArr = new String[sz]; for ( int i = 0; i < sz; i++ ) { newArr[i] = strip( strs[i], delimiter ); } return newArr; } /** *Strip any of a supplied String from the end of a String.
* *If the strip String is null
, whitespace is
* stripped.
Strip any of a supplied String from the start of a String.
* *If the strip String is null
, whitespace is
* stripped.
Convert a String to upper case, null
String
* returns null
.
Convert a String to lower case, null
String
* returns null
.
Uncapitalise a String.
* *That is, convert the first character into lower-case.
* null
is returned as null
.
Capitalise a String.
* *That is, convert the first character into title-case.
* null
is returned as null
.
Swaps the case of String.
* *Properly looks after making sure the start of words * are Titlecase and not Uppercase.
* *null
is returned as null
.
Capitalise all the words in a String.
* *Uses {@link Character#isWhitespace(char)} as a * separator between words.
* *null
will return null
.
Uncapitalise all the words in a string.
* *Uses {@link Character#isWhitespace(char)} as a * separator between words.
* *null
will return null
.
Get the String that is nested in between two instances of the * same String.
* *If str
is null
, will
* return null
.
null
* @throws NullPointerException if tag is null
*/
public static String getNestedString( String str, String tag )
{
return getNestedString( str, tag, tag );
}
/**
* Get the String that is nested in between two Strings.
* * @param str the String containing nested-string * @param open the String before nested-string * @param close the String after nested-string * @return the String that was nested, ornull
* @throws NullPointerException if open or close is null
*/
public static String getNestedString( String str, String open, String close )
{
if ( str == null )
{
return null;
}
int start = str.indexOf( open );
if ( start != -1 )
{
int end = str.indexOf( close, start + open.length() );
if ( end != -1 )
{
return str.substring( start + open.length(), end );
}
}
return null;
}
/**
* How many times is the substring in the larger String.
* *null
returns 0
.
null
* @throws NullPointerException if sub is null
*/
public static int countMatches( String str, String sub )
{
if ( sub.equals( "" ) )
{
return 0;
}
if ( str == null )
{
return 0;
}
int count = 0;
int idx = 0;
while ( ( idx = str.indexOf( sub, idx ) ) != -1 )
{
count++;
idx += sub.length();
}
return count;
}
// Character Tests
//--------------------------------------------------------------------------
/**
* Checks if the String contains only unicode letters.
* *null
will return false
.
* An empty String will return true
.
true
if only contains letters, and is non-null
*/
public static boolean isAlpha( String str )
{
if ( str == null )
{
return false;
}
int sz = str.length();
for ( int i = 0; i < sz; i++ )
{
if ( Character.isLetter( str.charAt( i ) ) == false )
{
return false;
}
}
return true;
}
/**
* Checks if the String contains only whitespace.
* *null
will return false
. An
* empty String will return true
.
true
if only contains whitespace, and is non-null
*/
public static boolean isWhitespace( String str )
{
if ( str == null )
{
return false;
}
int sz = str.length();
for ( int i = 0; i < sz; i++ )
{
if ( ( Character.isWhitespace( str.charAt( i ) ) == false ) )
{
return false;
}
}
return true;
}
/**
* Checks if the String contains only unicode letters and
* space (' '
).
null
will return false
. An
* empty String will return true
.
true
if only contains letters and space,
* and is non-null
*/
public static boolean isAlphaSpace( String str )
{
if ( str == null )
{
return false;
}
int sz = str.length();
for ( int i = 0; i < sz; i++ )
{
if ( ( Character.isLetter( str.charAt( i ) ) == false ) &&
( str.charAt( i ) != ' ' ) )
{
return false;
}
}
return true;
}
/**
* Checks if the String contains only unicode letters or digits.
* *null
will return false
. An empty
* String will return true
.
true
if only contains letters or digits,
* and is non-null
*/
public static boolean isAlphanumeric( String str )
{
if ( str == null )
{
return false;
}
int sz = str.length();
for ( int i = 0; i < sz; i++ )
{
if ( Character.isLetterOrDigit( str.charAt( i ) ) == false )
{
return false;
}
}
return true;
}
/**
* Checks if the String contains only unicode letters, digits
* or space (' '
).
null
will return false
. An empty
* String will return true
.
true
if only contains letters, digits or space,
* and is non-null
*/
public static boolean isAlphanumericSpace( String str )
{
if ( str == null )
{
return false;
}
int sz = str.length();
for ( int i = 0; i < sz; i++ )
{
if ( ( Character.isLetterOrDigit( str.charAt( i ) ) == false ) &&
( str.charAt( i ) != ' ' ) )
{
return false;
}
}
return true;
}
/**
* Checks if the String contains only unicode digits.
* *null
will return false
.
* An empty String will return true
.
true
if only contains digits, and is non-null
*/
public static boolean isNumeric( String str )
{
if ( str == null )
{
return false;
}
int sz = str.length();
for ( int i = 0; i < sz; i++ )
{
if ( Character.isDigit( str.charAt( i ) ) == false )
{
return false;
}
}
return true;
}
/**
* Checks if the String contains only unicode digits or space
* (' '
).
null
will return false
. An empty
* String will return true
.
true
if only contains digits or space,
* and is non-null
*/
public static boolean isNumericSpace( String str )
{
if ( str == null )
{
return false;
}
int sz = str.length();
for ( int i = 0; i < sz; i++ )
{
if ( ( Character.isDigit( str.charAt( i ) ) == false ) &&
( str.charAt( i ) != ' ' ) )
{
return false;
}
}
return true;
}
// Defaults
//--------------------------------------------------------------------------
/**
* Returns either the passed in Object
as a String,
* or, if the Object
is null
, an empty
* String.
null
*/
public static String defaultString( Object obj )
{
return defaultString( obj, "" );
}
/**
* Returns either the passed in Object
as a String,
* or, if the Object
is null
, a passed
* in default String.
null
* @return the passed in string, or the default if it was
* null
*/
public static String defaultString( Object obj, String defaultString )
{
return ( obj == null ) ? defaultString : obj.toString();
}
// Reversing
//--------------------------------------------------------------------------
/**
* Reverse a String.
* *null
String returns null
.
Reverses a String that is delimited by a specific character.
* *The Strings between the delimiters are not reversed.
* Thus java.lang.String becomes String.lang.java (if the delimiter
* is '.'
).
Reverses an array.
* *TAKEN FROM CollectionsUtils.
* * @param array the array to reverse */ private static void reverseArray( Object[] array ) { int i = 0; int j = array.length - 1; Object tmp; while ( j > i ) { tmp = array[j]; array[j] = array[i]; array[i] = tmp; j--; i++; } } // Abbreviating //-------------------------------------------------------------------------- /** * Turn "Now is the time for all good men" into "Now is the time for..." ** Specifically: *
* If str is less than max characters long, return it. * Else abbreviate it to (substring(str, 0, max-3) + "..."). * If maxWidth is less than 3, throw an IllegalArgumentException. * In no case will it return a string of length greater than maxWidth. * * @param maxWidth maximum length of result string **/ public static String abbreviate( String s, int maxWidth ) { return abbreviate( s, 0, maxWidth ); } /** * Turn "Now is the time for all good men" into "...is the time for..." *
* Works like abbreviate(String, int), but allows you to specify a "left edge" * offset. Note that this left edge is not necessarily going to be the leftmost * character in the result, or the first * character following the ellipses, but it will appear somewhere in the result. * In no case will it return a string of length greater than maxWidth. * * @param offset left edge of source string * @param maxWidth maximum length of result string **/ public static String abbreviate( String s, int offset, int maxWidth ) { if ( maxWidth < 4 ) { throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); } if ( s.length() <= maxWidth ) { return s; } if ( offset > s.length() ) { offset = s.length(); } if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) { offset = s.length() - ( maxWidth - 3 ); } if ( offset <= 4 ) { return s.substring( 0, maxWidth - 3 ) + "..."; } if ( maxWidth < 7 ) { throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); } if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) { return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); } return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); } // Difference //-------------------------------------------------------------------------- /** * Compare two strings, and return the portion where they differ. * (More precisely, return the remainder of the second string, * starting from where it's different from the first.) *
* E.g. strdiff("i am a machine", "i am a robot") -> "robot" * * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal **/ public static String difference( String s1, String s2 ) { int at = differenceAt( s1, s2 ); if ( at == -1 ) { return ""; } return s2.substring( at ); } /** * Compare two strings, and return the index at which the strings begin to differ. *
* E.g. strdiff("i am a machine", "i am a robot") -> 7 *
* * @return the index where s2 and s1 begin to differ; -1 if they are equal **/ public static int differenceAt( String s1, String s2 ) { int i; for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) { if ( s1.charAt( i ) != s2.charAt( i ) ) { break; } } if ( ( i < s2.length() ) || ( i < s1.length() ) ) { return i; } return -1; } public static String interpolate( String text, Map namespace ) { Iterator keys = namespace.keySet().iterator(); while ( keys.hasNext() ) { String key = keys.next().toString(); Object obj = namespace.get( key ); if ( obj == null ) { throw new NullPointerException( "The value of the key '" + key + "' is null." ); } String value = obj.toString(); text = StringUtils.replace( text, "${" + key + "}", value ); if ( key.indexOf( " " ) == -1 ) { text = StringUtils.replace( text, "$" + key, value ); } } return text; } public static String removeAndHump( String data, String replaceThis ) { String temp; StringBuffer out = new StringBuffer(); temp = data; StringTokenizer st = new StringTokenizer( temp, replaceThis ); while ( st.hasMoreTokens() ) { String element = (String) st.nextElement(); out.append( capitalizeFirstLetter( element ) ); } return out.toString(); } public static String capitalizeFirstLetter( String data ) { char firstLetter = Character.toTitleCase( data.substring( 0, 1 ).charAt( 0 ) ); String restLetters = data.substring( 1 ); return firstLetter + restLetters; } public static String lowercaseFirstLetter( String data ) { char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); String restLetters = data.substring( 1 ); return firstLetter + restLetters; } public static String addAndDeHump( String view ) { StringBuffer sb = new StringBuffer(); for ( int i = 0; i < view.length(); i++ ) { if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) { sb.append( '-' ); } sb.append( view.charAt( i ) ); } return sb.toString().trim().toLowerCase( Locale.ENGLISH ); } /** *Quote and escape a String with the given character, handling null
.
* StringUtils.quoteAndEscape(null, *) = null * StringUtils.quoteAndEscape("", *) = "" * StringUtils.quoteAndEscape("abc", '"') = abc * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc" * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc' ** * @param source * @param quoteChar * @return the String quoted and escaped * @since 1.5.1 * @see #quoteAndEscape(String, char, char[], char[], char, boolean) */ public static String quoteAndEscape( String source, char quoteChar ) { return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); } /** *
Quote and escape a String with the given character, handling null
.
Checks if String contains a search character, handling null
.
* This method uses {@link String#indexOf(int)}.
A null
or empty ("") String will return false
.
* StringUtils.contains(null, *) = false * StringUtils.contains("", *) = false * StringUtils.contains("abc", 'a') = true * StringUtils.contains("abc", 'z') = false ** * @param str the String to check, may be null * @param searchChar the character to find * @return true if the String contains the search character, * false if not or
null
string input
* @since 1.5.7
*/
public static boolean contains( String str, char searchChar )
{
if ( isEmpty( str ) )
{
return false;
}
return str.indexOf( searchChar ) >= 0;
}
/**
* Checks if String contains a search String, handling null
.
* This method uses {@link String#indexOf(int)}.
A null
String will return false
.
* StringUtils.contains(null, *) = false * StringUtils.contains(*, null) = false * StringUtils.contains("", "") = true * StringUtils.contains("abc", "") = true * StringUtils.contains("abc", "a") = true * StringUtils.contains("abc", "z") = false ** * @param str the String to check, may be null * @param searchStr the String to find, may be null * @return true if the String contains the search String, * false if not or
null
string input
* @since 1.5.7
*/
public static boolean contains( String str, String searchStr )
{
if ( str == null || searchStr == null )
{
return false;
}
return str.indexOf( searchStr ) >= 0;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/SweeperPool.java 0000664 0000000 0000000 00000022512 11660305702 0026702 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.util.ArrayList;
/**
* Pools a bunch of objects . Runs a sweeper periodically to
* keep it down to size. The objects in the pool first get disposed first.
*
* @author Bert van Brakel
* @version $Id: SweeperPool.java 8010 2009-01-07 12:59:50Z vsiveton $
*/
public class SweeperPool
{
/***/
private static final boolean DEBUG = false;
/** Sweeps the pool periodically to trim it's size */
private transient Sweeper sweeper;
/** Absolute maxiumuim size of the pool.*/
private transient int maxSize;
/** The size the pool gets trimmed down to */
private transient int minSize;
/** When the sweeper runs
* and the pool is over this size, then the pool is trimmed */
private int triggerSize;
/** Holds the pooled objects */
private ArrayList pooledObjects;
/** Flag indicating this pool is shuting down */
private boolean shuttingDown = false;
//private Vector used;
/**
*
* There are a number of settings to control how the pool operates. *
minSize
- this is the size the pool is trimmed totriggerSize
- this determines if the pool is trimmed when
* the sweeper runs. If the pool size is greater or equal than this value then
* the pool is trimmed to minSize
.
* maxSize
- if the pool has reached this size, any objects added
* are immediatley disposed. If the pool is this size when the sweeper runs, then
* the pool is also trimmed to minSize
irrespective of the triggerSize.
* sweepInterval
- how often the sweeper runs. Is actually the
* time since the sweeper last finished a pass. 0 if the sweeper should not run.
* Any value less than 0 is automatically converted to 0
*/ public SweeperPool( int maxSize, int minSize, int intialCapacity, int sweepInterval, int triggerSize ) { super(); this.maxSize = saneConvert( maxSize ); this.minSize = saneConvert( minSize ); this.triggerSize = saneConvert( triggerSize ); pooledObjects = new ArrayList( intialCapacity ); //only run a sweeper if sweep interval is positive if ( sweepInterval > 0 ) { sweeper = new Sweeper( this, sweepInterval ); sweeper.start(); } } private int saneConvert( int value ) { if ( value < 0 ) { return 0; } else { return value; } } /** * Return the pooled object */ public synchronized Object get() { if ( ( pooledObjects.size() == 0 ) || shuttingDown ) { return null; } else { Object obj = pooledObjects.remove( 0 ); objectRetrieved( obj ); //used.add(obj); return obj; } } /** * Add an object to the pool * * @param obj the object to pool. Can be null. * * @return true if the object was added to the pool, false if it was disposed or null * */ public synchronized boolean put( Object obj ) { objectAdded( obj ); if ( ( obj != null ) && ( pooledObjects.size() < maxSize ) && ( shuttingDown == false ) ) { pooledObjects.add( obj ); return true; } else if ( obj != null ) { //no longer need the object, so dispose it objectDisposed( obj ); } return false; } /** * Return the number of pooled objects. This is never * greater than t maximuim size of the pool * * @return the number of pooled objects */ public synchronized int getSize() { return pooledObjects.size(); } /** * Dispose of this pool. Stops the sweeper and disposes each object in the pool * */ public void dispose() { shuttingDown = true; if ( sweeper != null ) { sweeper.stop(); try { sweeper.join(); } catch ( InterruptedException e ) { System.err.println( "Unexpected execption occurred: " ); e.printStackTrace(); } } synchronized ( this ) { // use an array here as objects may still be being put back in the pool // and we don't want to throw a ConcurrentModificationException Object[] objects = pooledObjects.toArray(); for ( int i = 0; i < objects.length; i++ ) { objectDisposed( objects[i] ); } pooledObjects.clear(); } } /** * A pool has been disposed if has been shutdown and the sweeper has completed running. * * @return true if the pool has been disposed, false otherwise */ boolean isDisposed() { if ( !shuttingDown ) { return false; } // A null sweeper means one was never started. if ( sweeper == null ) { return true; } return sweeper.hasStopped(); } /** * Trim the pool down to min size * */ public synchronized void trim() { if ( ( ( triggerSize > 0 ) && ( pooledObjects.size() >= triggerSize ) ) || ( ( maxSize > 0 ) && ( pooledObjects.size() >= maxSize ) ) ) { while ( pooledObjects.size() > minSize ) { objectDisposed( pooledObjects.remove( 0 ) ); } } } /** * Override this to be notified of object disposal. Called * after the object has been removed. Occurs when the pool * is trimmed. * * @param obj */ public void objectDisposed( Object obj ) { } /** * Override this to be notified of object addition. * Called before object is to be added. * * @param obj */ public void objectAdded( Object obj ) { } /** * Override this to be notified of object retrieval. * Called after object removed from the pool, but * before returned to the client. * * @param obj */ public void objectRetrieved( Object obj ) { } /** * Periodically atsweepInterval
goes through
* and tests if the pool should be trimmed.
*
* @author bert
*
*/
private static class Sweeper implements Runnable
{
private final transient SweeperPool pool;
private transient boolean service = false;
private final transient int sweepInterval;
private transient Thread t = null;
/**
*
*/
public Sweeper( SweeperPool pool, int sweepInterval )
{
super();
this.sweepInterval = sweepInterval;
this.pool = pool;
}
/**
* Run the seeper.
*
* @see java.lang.Runnable#run()
*/
public void run()
{
debug( "started" );
if ( sweepInterval > 0 )
{
synchronized ( this )
{
while ( service )
{
try
{
//wait specified number of seconds
//before running next sweep
wait( sweepInterval * 1000 );
}
catch ( InterruptedException e )
{
}
runSweep();
}
}
}
debug( "stopped" );
}
public void start()
{
if ( !service )
{
service = true;
t = new Thread( this );
t.setName( "Sweeper" );
t.start();
}
}
public synchronized void stop()
{
service = false;
notifyAll();
}
void join()
throws InterruptedException
{
t.join();
}
boolean hasStopped()
{
return !service && !t.isAlive();
}
private final void debug( String msg )
{
if ( DEBUG )
{
System.err.println( this + ":" + msg );
}
}
private void runSweep()
{
debug( "runningSweep. time=" + System.currentTimeMillis() );
pool.trim();
}
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/TypeFormat.java 0000664 0000000 0000000 00000105411 11660305702 0026530 0 ustar 00root root 0000000 0000000 /*
* J.A.D.E. Java(TM) Addition to Default Environment.
* Latest release available at http://jade.dautelle.com/
* This class is public domain (not copyrighted).
*/
package org.codehaus.plexus.util;
/**
* This class provides utility methods to parse CharSequence
* into primitive types and to format primitive types into
* StringBuffer
.
Methods from this utility class do not create temporary objects
* and are typically faster than standard library methods (e.g {@link
* #parseDouble} is up to 15x faster than Double.parseDouble
).
*
For class instances, formatting is typically performed using specialized
* java.text.Format
(Locale
sensitive)
* and/or using conventional methods (class sensitive). For example:
* public class Foo { * public static Foo valueOf(CharSequence chars) {...} // Parses. * public StringBuffer appendTo(StringBuffer sb) {...} // Formats. * public String toString() { * return appendTo(new StringBuffer()).toString(); * } * }* *
This class is public domain (not copyrighted).
* * @author Jean-Marie Dautelle * @version 4.6, June 22, 2003 */ public final class TypeFormat { /** * Holds the characters used to represent numbers. */ private final static char[] DIGITS = { '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' }; /** * Default constructor (forbids derivation). */ private TypeFormat() {} /** * Searches for a particular sequence within a character sequence * (general purpose parsing function). * * @param pattern the character sequence to search for. * @param chars the character sequence being searched. * @param fromIndex the index inchars
to start the search
* from.
* @return the index in the range
* [fromIndex, chars.length()-pattern.length()]
* or -1
if the character sequence is not found.
*/
public static int indexOf(CharSequence pattern, CharSequence chars,
int fromIndex) {
int patternLength = pattern.length();
fromIndex = Math.max(0, fromIndex);
if (patternLength != 0) { // At least one character to search for.
char firstChar = pattern.charAt(0);
int last = chars.length() - patternLength;
for (int i=fromIndex; i <= last; i++) {
if (chars.charAt(i) == firstChar) {
boolean match = true;
for (int j=1; j < patternLength; j++) {
if (chars.charAt(i+j) != pattern.charAt(j)) {
match = false;
break;
}
}
if (match) {
return i;
}
}
}
return -1;
} else {
return Math.min(0, fromIndex);
}
}
/**
* Parses the specified CharSequence
as a boolean
.
*
* @param chars the character sequence to parse.
* @return the corresponding boolean
.
*/
public static boolean parseBoolean(CharSequence chars) {
return (chars.length() == 4) &&
(chars.charAt(0) == 't' || chars.charAt(0) == 'T') &&
(chars.charAt(1) == 'r' || chars.charAt(1) == 'R') &&
(chars.charAt(2) == 'u' || chars.charAt(2) == 'U') &&
(chars.charAt(3) == 'e' || chars.charAt(3) == 'E');
}
/**
* Parses the specified CharSequence
as a signed decimal
* short
.
*
* @param chars the character sequence to parse.
* @return parseShort(chars, 10)
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable short
.
* @see #parseShort(CharSequence, int)
*/
public static short parseShort(CharSequence chars) {
return parseShort(chars, 10);
}
/**
* Parses the specified CharSequence
as a signed
* short
in the specified radix. The characters in the string
* must all be digits of the specified radix, except the first character
* which may be a plus sign '+'
or a minus sign
* '-'
.
*
* @param chars the character sequence to parse.
* @param radix the radix to be used while parsing.
* @return the corresponding short
.
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable short
.
*/
public static short parseShort(CharSequence chars, int radix) {
try {
boolean isNegative = (chars.charAt(0) == '-') ? true : false;
int result = 0;
int limit = (isNegative) ? Short.MIN_VALUE : -Short.MAX_VALUE;
int multmin = limit / radix;
int length = chars.length();
int i = (isNegative || (chars.charAt(0) == '+')) ? 1 : 0;
while (true) {
int digit = Character.digit(chars.charAt(i), radix);
int tmp = result * radix;
if ((digit < 0) || (result < multmin) ||
(tmp < limit + digit)) { // Overflow.
throw new NumberFormatException(
"For input characters: \"" + chars.toString() + "\"");
}
// Accumulates negatively.
result = tmp - digit;
if (++i >= length) {
break;
}
}
return (short) (isNegative ? result : -result);
} catch (IndexOutOfBoundsException e) {
throw new NumberFormatException(
"For input characters: \"" + chars.toString() + "\"");
}
}
/**
* Parses the specified CharSequence
as a signed decimal
* int
.
*
* @param chars the character sequence to parse.
* @return parseInt(chars, 10)
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable int
.
* @see #parseInt(CharSequence, int)
*/
public static int parseInt(CharSequence chars) {
return parseInt(chars, 10);
}
/**
* Parses the specified CharSequence
as a signed
* int
in the specified radix. The characters in the string
* must all be digits of the specified radix, except the first character
* which may be a plus sign '+'
or a minus sign
* '-'
.
*
* @param chars the character sequence to parse.
* @param radix the radix to be used while parsing.
* @return the corresponding int
.
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable int
.
*/
public static int parseInt(CharSequence chars, int radix) {
try {
boolean isNegative = (chars.charAt(0) == '-') ? true : false;
int result = 0;
int limit = (isNegative) ? Integer.MIN_VALUE : -Integer.MAX_VALUE;
int multmin = limit / radix;
int length = chars.length();
int i = (isNegative || (chars.charAt(0) == '+')) ? 1 : 0;
while (true) {
int digit = Character.digit(chars.charAt(i), radix);
int tmp = result * radix;
if ((digit < 0) || (result < multmin) ||
(tmp < limit + digit)) { // Overflow.
throw new NumberFormatException(
"For input characters: \"" + chars.toString() + "\"");
}
// Accumulates negatively to avoid surprises near MAX_VALUE
result = tmp - digit;
if (++i >= length) {
break;
}
}
return isNegative ? result : -result;
} catch (IndexOutOfBoundsException e) {
throw new NumberFormatException(
"For input characters: \"" + chars.toString() + "\"");
}
}
/**
* Parses the specified CharSequence
as a signed decimal
* long
.
*
* @param chars the character sequence to parse.
* @return parseLong(chars, 10)
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable long
.
* @see #parseLong(CharSequence, int)
*/
public static long parseLong(CharSequence chars) {
return parseLong(chars, 10);
}
/**
* Parses the specified CharSequence
as a signed
* long
in the specified radix. The characters in the string
* must all be digits of the specified radix, except the first character
* which may be a plus sign '+'
or a minus sign
* '-'
.
*
* @param chars the character sequence to parse.
* @param radix the radix to be used while parsing.
* @return the corresponding long
.
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable long
.
*/
public static long parseLong(CharSequence chars, int radix) {
try {
boolean isNegative = (chars.charAt(0) == '-') ? true : false;
long result = 0;
long limit = (isNegative) ? Long.MIN_VALUE : -Long.MAX_VALUE;
long multmin = limit / radix;
int length = chars.length();
int i = (isNegative || (chars.charAt(0) == '+')) ? 1 : 0;
while (true) {
int digit = Character.digit(chars.charAt(i), radix);
long tmp = result * radix;
if ((digit < 0) || (result < multmin) ||
(tmp < limit + digit)) { // Overflow.
throw new NumberFormatException(
"For input characters: \"" + chars.toString() + "\"");
}
// Accumulates negatively to avoid surprises near MAX_VALUE
result = tmp - digit;
if (++i >= length) {
break;
}
}
return isNegative ? result : -result;
} catch (IndexOutOfBoundsException e) {
throw new NumberFormatException(
"For input characters: \"" + chars.toString() + "\"");
}
}
/**
* Parses this CharSequence
as a float
.
*
* @param chars the character sequence to parse.
* @return the float number represented by the specified character sequence.
* @throws NumberFormatException if the character sequence does not contain
* a parsable float
.
*/
public static float parseFloat(CharSequence chars) {
double d = parseDouble(chars);
if ( (d >= Float.MIN_VALUE) && (d <= Float.MAX_VALUE)) {
return (float) d;
} else {
throw new NumberFormatException(
"Float overflow for input characters: \"" +
chars.toString() + "\"");
}
}
/**
* Parses this CharSequence
as a double
.
*
* @param chars the character sequence to parse.
* @return the double number represented by this character sequence.
* @throws NumberFormatException if the character sequence does not contain
* a parsable double
.
*/
public static double parseDouble(CharSequence chars)
throws NumberFormatException {
try {
int length = chars.length();
double result = 0.0;
int exp = 0;
boolean isNegative = (chars.charAt(0) == '-') ? true : false;
int i = (isNegative || (chars.charAt(0) == '+')) ? 1 : 0;
// Checks special cases NaN or Infinity.
if ((chars.charAt(i) == 'N') || (chars.charAt(i) == 'I')) {
if (chars.toString().equals("NaN")) {
return Double.NaN;
} else if (chars.subSequence(i, length).toString().
equals("Infinity")) {
return isNegative ? Double.NEGATIVE_INFINITY :
Double.POSITIVE_INFINITY;
}
}
// Reads decimal number.
boolean fraction = false;
while (true) {
char c = chars.charAt(i);
if ( (c == '.') && (!fraction)) {
fraction = true;
} else if ((c == 'e') || (c == 'E')) {
break;
} else if ((c >= '0') && (c <= '9')) {
result = result * 10 + (c - '0');
if (fraction) {
exp--;
}
} else {
throw new NumberFormatException(
"For input characters: \"" + chars.toString() + "\"");
}
if (++i >= length) {
break;
}
}
result = isNegative ? - result : result;
// Reads exponent (if any).
if (i < length) {
i++;
boolean negE = (chars.charAt(i) == '-') ? true : false;
i = (negE || (chars.charAt(i) == '+')) ? i+1 : i;
int valE = 0;
while (true) {
char c = chars.charAt(i);
if ((c >= '0') && (c <= '9')) {
valE = valE * 10 + (c - '0');
if (valE > 10000000) { // Hard-limit to avoid overflow.
valE = 10000000;
}
} else {
throw new NumberFormatException(
"For input characters: \"" + chars.toString() +
"\"");
}
if (++i >= length) {
break;
}
}
exp += negE ? -valE : valE;
}
// Returns product decimal number with exponent.
return multE(result, exp);
} catch (IndexOutOfBoundsException e) {
throw new NumberFormatException(
"For input characters: \"" + chars.toString() + "\"");
}
}
/**
* Formats the specified boolean
and appends the resulting
* text to the StringBuffer
argument.
*
* @param b a boolean
.
* @param sb the StrinBuffer
to append.
* @return the specified StringBuffer
object.
* @see #parseBoolean
*/
public static StringBuffer format(boolean b, StringBuffer sb) {
return b ? sb.append("true") : sb.append("false");
}
/**
* Formats the specified short
and appends the resulting
* text (decimal representation) to the StringBuffer
argument.
*
* Note: This method is preferred to StringBuffer.append(short)
*
as it does not create temporary String
* objects (several times faster for small numbers).
short
number.
* @param sb the StrinBuffer
to append.
* @return the specified StringBuffer
object.
* @see #parseShort
*/
public static StringBuffer format(short s, StringBuffer sb) {
return format((int)s, sb); // Forwards to int formatting (fast).
}
/**
* Formats the specified short
in the specified radix and
* appends the resulting text to the StringBuffer
argument.
*
* @param s the short
number.
* @param radix the radix.
* @param sb the StrinBuffer
to append.
* @return the specified StringBuffer
object.
* @see #parseShort(CharSequence, int)
* throws IllegalArgumentException if radix is not in [2 .. 36] range.
*/
public static StringBuffer format(short s, int radix, StringBuffer sb) {
return format((int)s, radix, sb); // Forwards to int formatting (fast).
}
/**
* Formats the specified int
and appends the resulting
* text (decimal representation) to the StringBuffer
argument.
*
* Note: This method is preferred to StringBuffer.append(int)
*
as it does not create temporary String
* objects (several times faster for small numbers).
int
number.
* @param sb the StrinBuffer
to append.
* @return the specified StringBuffer
object.
* @see #parseInt
*/
public static StringBuffer format(int i, StringBuffer sb) {
if (i <= 0) {
if (i == Integer.MIN_VALUE) { // Negation would overflow.
return sb.append("-2147483648"); // 11 char max.
} else if (i == 0) {
return sb.append('0');
}
i = -i;
sb.append('-');
}
int j = 1;
for (; (j < 10) && (i >= INT_POW_10[j]); j++) {}
// POW_10[j] > i >= POW_10[j-1]
for (j--; j >= 0; j--) {
int pow10 = INT_POW_10[j];
int digit = i / pow10;
i -= digit * pow10;
sb.append(DIGITS[digit]);
}
return sb;
}
private static final int[] INT_POW_10 = new int[10];
static {
int pow = 1;
for (int i=0; i < 10; i++) {
INT_POW_10[i] = pow;
pow *= 10;
}
}
/**
* Formats the specified int
in the specified radix and appends
* the resulting text to the StringBuffer
argument.
*
* @param i the int
number.
* @param radix the radix.
* @param sb the StrinBuffer
to append.
* @return the specified StringBuffer
object.
* @see #parseInt(CharSequence, int)
* throws IllegalArgumentException if radix is not in [2 .. 36] range.
*/
public static StringBuffer format(int i, int radix, StringBuffer sb) {
if (radix == 10) {
return format(i, sb); // Faster version.
} else if (radix < 2 || radix > 36) {
throw new IllegalArgumentException("radix: " + radix);
}
if (i < 0) {
sb.append('-');
} else {
i = -i;
}
format2(i, radix, sb);
return sb;
}
private static void format2(int i, int radix, StringBuffer sb) {
if (i <= -radix) {
format2(i / radix, radix, sb);
sb.append(DIGITS[-(i % radix)]);
} else {
sb.append(DIGITS[-i]);
}
}
/**
* Formats the specified long
and appends the resulting
* text (decimal representation) to the StringBuffer
argument.
*
* Note: This method is preferred to StringBuffer.append(long)
*
as it does not create temporary String
* objects (several times faster for small numbers).
long
number.
* @param sb the StrinBuffer
to append.
* @return the specified StringBuffer
object.
* @see #parseLong
*/
public static StringBuffer format(long l, StringBuffer sb) {
if (l <= 0) {
if (l == Long.MIN_VALUE) { // Negation would overflow.
return sb.append("-9223372036854775808"); // 20 characters max.
} else if (l == 0) {
return sb.append('0');
}
l = -l;
sb.append('-');
}
int j = 1;
for (; (j < 19) && (l >= LONG_POW_10[j]); j++) {}
// POW_10[j] > l >= POW_10[j-1]
for (j--; j >= 0; j--) {
long pow10 = LONG_POW_10[j];
int digit = (int) (l / pow10);
l -= digit * pow10;
sb.append(DIGITS[digit]);
}
return sb;
}
private static final long[] LONG_POW_10 = new long[19];
static {
long pow = 1;
for (int i=0; i < 19; i++) {
LONG_POW_10[i] = pow;
pow *= 10;
}
}
/**
* Formats the specified long
in the specified radix and
* appends the resulting text to the StringBuffer
argument.
*
* @param l the long
number.
* @param radix the radix.
* @param sb the StrinBuffer
to append.
* @return the specified StringBuffer
object.
* @see #parseLong(CharSequence, int)
* throws IllegalArgumentException if radix is not in [2 .. 36] range.
*/
public static StringBuffer format(long l, int radix, StringBuffer sb) {
if (radix == 10) {
return format(l, sb); // Faster version.
} else if (radix < 2 || radix > 36) {
throw new IllegalArgumentException("radix: " + radix);
}
if (l < 0) {
sb.append('-');
} else {
l = -l;
}
format2(l, radix, sb);
return sb;
}
private static void format2(long l, int radix, StringBuffer sb) {
if (l <= -radix) {
format2(l / radix, radix, sb);
sb.append(DIGITS[(int)-(l % radix)]);
} else {
sb.append(DIGITS[(int)-l]);
}
}
/**
* Formats the specified float
and appends the resulting
* text to the StringBuffer
argument.
*
* @param f the float
number.
* @param sb the StrinBuffer
to append.
* @return format(f, 0.0f, sb)
* @see #format(float, float, StringBuffer)
*/
public static StringBuffer format(float f, StringBuffer sb) {
return format(f, 0.0f, sb);
}
/**
* Formats the specified float
and appends the resulting text
* to the StringBuffer
argument; the number of significative
* digits is deduced from the specifed precision. All digits at least as
* significant as the specified precision are represented. For example:
* format(5.6f, 0.01f, sb)
appends "5.60"
format(5.6f, 0.1f, sb)
appends "5.6"
format(5.6f, 1f, sb)
appends "6"
0.0f
, the precision is assumed to be
* the intrinsic float
precision (64 bits IEEE 754 format);
* no formatting is performed, all significant digits are displayed and
* trailing zeros are removed.
*
* @param f the float
number.
* @param precision the maximum weight of the last digit represented.
* @param sb the StrinBuffer
to append.
* @return the specified StringBuffer
object.
* @throws IllegalArgumentException if the specified precision is negative
* or would result in too many digits (19+).
*/
public static StringBuffer format(float f, float precision,
StringBuffer sb) {
// Adjusts precision.
boolean precisionOnLastDigit;
if (precision > 0.0f) {
precisionOnLastDigit = true;
} else if (precision == 0.0f) {
if (f != 0.0f) {
precisionOnLastDigit = false;
precision = Math.max(Math.abs(f * FLOAT_RELATIVE_ERROR),
Float.MIN_VALUE);
} else {
return sb.append("0.0"); // Exact zero.
}
} else {
throw new IllegalArgumentException(
"precision: Negative values not allowed");
}
return format(f, precision, precisionOnLastDigit, sb);
}
/**
* Formats the specified double
and appends the resulting
* text to the StringBuffer
argument.
*
* Note : This method is preferred to StringBuffer.append(double)
*
or even String.valueOf(double)
as it
* does not create temporary String
or
* FloatingDecimal
objects (several times faster,
* e.g. 15x faster for Double.MAX_VALUE
).
double
number.
* @param sb the StrinBuffer
to append.
* @return format(d, 0.0, sb)
* @see #format(double, double, StringBuffer)
*/
public static StringBuffer format(double d, StringBuffer sb) {
return format(d, 0.0, sb);
}
/**
* Formats the specified double
and appends the resulting text
* to the StringBuffer
argument; the number of significand
* digits is specified as integer argument.
*
* @param d the double
number.
* @param digits the number of significand digits (excludes exponent).
* @param sb the StrinBuffer
to append.
* @return the specified StringBuffer
object.
* @throws IllegalArgumentException if the number of digits is not in
* range [1..19]
.
*/
public static StringBuffer format(double d, int digits,
StringBuffer sb) {
if ((digits >= 1) && (digits <= 19)) {
double precision = Math.abs(d / DOUBLE_POW_10[digits-1]);
return format(d, precision, sb);
} else {
throw new java.lang.IllegalArgumentException(
"digits: " + digits + " is not in range [1 .. 19]");
}
}
/**
* Formats the specified double
and appends the resulting text
* to the StringBuffer
argument; the number of significative
* digits is deduced from the specifed precision. All digits at least as
* significant as the specified precision are represented. For example:
* format(5.6, 0.01, sb)
appends "5.60"
format(5.6, 0.1, sb)
appends "5.6"
format(5.6, 1, sb)
appends "6"
0.0
, the precision is assumed to be
* the intrinsic double
precision (64 bits IEEE 754 format);
* no formatting is performed, all significant digits are displayed and
* trailing zeros are removed.
*
* @param d the double
number.
* @param precision the maximum weight of the last digit represented.
* @param sb the StrinBuffer
to append.
* @return the specified StringBuffer
object.
* @throws IllegalArgumentException if the specified precision is negative
* or would result in too many digits (19+).
*/
public static StringBuffer format(double d, double precision,
StringBuffer sb) {
// Adjusts precision.
boolean precisionOnLastDigit = false;
if (precision > 0.0) {
precisionOnLastDigit = true;
} else if (precision == 0.0) {
if (d != 0.0) {
precision = Math.max(Math.abs(d * DOUBLE_RELATIVE_ERROR),
Double.MIN_VALUE);
} else {
return sb.append("0.0"); // Exact zero.
}
} else if (precision < 0.0) { // Not NaN
throw new IllegalArgumentException(
"precision: Negative values not allowed");
}
return format(d, precision, precisionOnLastDigit, sb);
}
/**
* Formats the specified double
and appends the resulting text
* to the StringBuffer
argument; the number of significative
* digits is deduced from the specifed precision.
*
* @param d the double
number.
* @param precision the maximum weight of the last digit represented.
* @param precisionOnLastDigit indicates if the number of digits is
* deduced from the specified precision.
* @param sb the StrinBuffer
to append.
* @return the specified StringBuffer
object.
*/
private static StringBuffer format(double d, double precision,
boolean precisionOnLastDigit,
StringBuffer sb) {
// Special cases.
if (Double.isNaN(d)) {
return sb.append("NaN");
} else if (Double.isInfinite(d)) {
return (d >= 0) ? sb.append("Infinity") : sb.append("-Infinity");
}
if (d < 0) {
d = -d;
sb.append('-');
}
// Formats decimal part.
int rank = (int) Math.floor(Math.log(precision) / LOG_10);
double digitValue = multE(d, -rank);
if (digitValue >= Long.MAX_VALUE) {
throw new IllegalArgumentException(
"Specified precision would result in too many digits");
}
int digitStart = sb.length();
format(Math.round(digitValue), sb);
int digitLength = sb.length() - digitStart;
int dotPos = digitLength + rank;
boolean useScientificNotation = false;
// Inserts dot ('.')
if ((dotPos <= -LEADING_ZEROS.length) || (dotPos > digitLength)) {
// Scientific notation has to be used ("x.xxxEyy").
sb.insert(digitStart + 1, '.');
useScientificNotation = true;
} else if (dotPos > 0) {
// Dot within the string ("xxxx.xxxxx").
sb.insert(digitStart + dotPos, '.');
} else {
// Leading zeros ("0.xxxxx").
sb.insert(digitStart, LEADING_ZEROS[-dotPos]);
}
// Removes trailing zeros.
if (!precisionOnLastDigit) {
int newLength = sb.length();
do {
newLength--;
} while (sb.charAt(newLength) == '0');
sb.setLength(newLength+1);
}
// Avoids trailing '.'
if (sb.charAt(sb.length()-1) == '.') {
if (precisionOnLastDigit) {
sb.setLength(sb.length()-1); // Prefers "xxx" to "xxx."
} else {
sb.append('0'); // Prefer "xxx.0" to "xxx."
}
}
// Writes exponent.
if (useScientificNotation) {
sb.append('E');
format(dotPos - 1, sb);
}
return sb;
}
private static final double LOG_10 = Math.log(10);
private static final float FLOAT_RELATIVE_ERROR = (float) Math.pow(2, -24);
private static final double DOUBLE_RELATIVE_ERROR = Math.pow(2, -53);
private static String[] LEADING_ZEROS = {"0.", "0.0", "0.00"};
/**
* Returns the product of the specified value with 10
raised
* at the specified power exponent.
*
* @param value the value.
* @param E the exponent.
* @return value * 10^E
*/
private static final double multE(double value, int E) {
if (E >= 0) {
if (E <= 308) {
// Max: 1.7976931348623157E+308
return value * DOUBLE_POW_10[E];
} else {
value *= 1E21; // Exact multiplicand.
E = Math.min(308, E-21);
return value * DOUBLE_POW_10[E];
}
} else {
if (E >= -308) {
return value / DOUBLE_POW_10[-E];
} else {
// Min: 4.9E-324
value /= 1E21; // Exact divisor.
E = Math.max(-308, E+21);
return value / DOUBLE_POW_10[-E];
}
}
}
// Note: Approximation for exponents > 21. This may introduce round-off
// errors (e.g. 1E23 represented as "9.999999999999999E22").
private static final double[] DOUBLE_POW_10 = new double[] {
1E000, 1E001, 1E002, 1E003, 1E004, 1E005, 1E006, 1E007, 1E008, 1E009,
1E010, 1E011, 1E012, 1E013, 1E014, 1E015, 1E016, 1E017, 1E018, 1E019,
1E020, 1E021, 1E022, 1E023, 1E024, 1E025, 1E026, 1E027, 1E028, 1E029,
1E030, 1E031, 1E032, 1E033, 1E034, 1E035, 1E036, 1E037, 1E038, 1E039,
1E040, 1E041, 1E042, 1E043, 1E044, 1E045, 1E046, 1E047, 1E048, 1E049,
1E050, 1E051, 1E052, 1E053, 1E054, 1E055, 1E056, 1E057, 1E058, 1E059,
1E060, 1E061, 1E062, 1E063, 1E064, 1E065, 1E066, 1E067, 1E068, 1E069,
1E070, 1E071, 1E072, 1E073, 1E074, 1E075, 1E076, 1E077, 1E078, 1E079,
1E080, 1E081, 1E082, 1E083, 1E084, 1E085, 1E086, 1E087, 1E088, 1E089,
1E090, 1E091, 1E092, 1E093, 1E094, 1E095, 1E096, 1E097, 1E098, 1E099,
1E100, 1E101, 1E102, 1E103, 1E104, 1E105, 1E106, 1E107, 1E108, 1E109,
1E110, 1E111, 1E112, 1E113, 1E114, 1E115, 1E116, 1E117, 1E118, 1E119,
1E120, 1E121, 1E122, 1E123, 1E124, 1E125, 1E126, 1E127, 1E128, 1E129,
1E130, 1E131, 1E132, 1E133, 1E134, 1E135, 1E136, 1E137, 1E138, 1E139,
1E140, 1E141, 1E142, 1E143, 1E144, 1E145, 1E146, 1E147, 1E148, 1E149,
1E150, 1E151, 1E152, 1E153, 1E154, 1E155, 1E156, 1E157, 1E158, 1E159,
1E160, 1E161, 1E162, 1E163, 1E164, 1E165, 1E166, 1E167, 1E168, 1E169,
1E170, 1E171, 1E172, 1E173, 1E174, 1E175, 1E176, 1E177, 1E178, 1E179,
1E180, 1E181, 1E182, 1E183, 1E184, 1E185, 1E186, 1E187, 1E188, 1E189,
1E190, 1E191, 1E192, 1E193, 1E194, 1E195, 1E196, 1E197, 1E198, 1E199,
1E200, 1E201, 1E202, 1E203, 1E204, 1E205, 1E206, 1E207, 1E208, 1E209,
1E210, 1E211, 1E212, 1E213, 1E214, 1E215, 1E216, 1E217, 1E218, 1E219,
1E220, 1E221, 1E222, 1E223, 1E224, 1E225, 1E226, 1E227, 1E228, 1E229,
1E230, 1E231, 1E232, 1E233, 1E234, 1E235, 1E236, 1E237, 1E238, 1E239,
1E240, 1E241, 1E242, 1E243, 1E244, 1E245, 1E246, 1E247, 1E248, 1E249,
1E250, 1E251, 1E252, 1E253, 1E254, 1E255, 1E256, 1E257, 1E258, 1E259,
1E260, 1E261, 1E262, 1E263, 1E264, 1E265, 1E266, 1E267, 1E268, 1E269,
1E270, 1E271, 1E272, 1E273, 1E274, 1E275, 1E276, 1E277, 1E278, 1E279,
1E280, 1E281, 1E282, 1E283, 1E284, 1E285, 1E286, 1E287, 1E288, 1E289,
1E290, 1E291, 1E292, 1E293, 1E294, 1E295, 1E296, 1E297, 1E298, 1E299,
1E300, 1E301, 1E302, 1E303, 1E304, 1E305, 1E306, 1E307, 1E308 };
} plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/WriterFactory.java 0000664 0000000 0000000 00000013723 11660305702 0027246 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.charset.Charset;
import org.codehaus.plexus.util.xml.XmlStreamWriter;
/**
* Utility to create Writers, with explicit encoding choice: platform default,
* XML, or specified.
*
* @author Herve Boutemy
* @see Charset
* @see Supported encodings
* @version $Id: WriterFactory.java 8289 2009-07-18 17:27:30Z hboutemy $
* @since 1.4.4
*/
public class WriterFactory
{
/**
* ISO Latin Alphabet #1, also known as ISO-LATIN-1.
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String ISO_8859_1 = "ISO-8859-1";
/**
* Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set.
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String US_ASCII = "US-ASCII";
/**
* Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either
* order accepted on input, big-endian used on output).
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String UTF_16 = "UTF-16";
/**
* Sixteen-bit Unicode Transformation Format, big-endian byte order.
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String UTF_16BE = "UTF-16BE";
/**
* Sixteen-bit Unicode Transformation Format, little-endian byte order.
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String UTF_16LE = "UTF-16LE";
/**
* Eight-bit Unicode Transformation Format.
* Every implementation of the Java platform is required to support this character encoding.
* @see Charset
*/
public static final String UTF_8 = "UTF-8";
/**
* The file.encoding
System Property.
*/
public static final String FILE_ENCODING = System.getProperty( "file.encoding" );
/**
* Create a new Writer with XML encoding detection rules.
*
* @param out not null output stream.
* @return an XML writer instance for the output stream.
* @throws IOException if any.
* @see XmlStreamWriter
*/
public static XmlStreamWriter newXmlWriter( OutputStream out )
throws IOException
{
return new XmlStreamWriter( out );
}
/**
* Create a new Writer with XML encoding detection rules.
*
* @param file not null file.
* @return an XML writer instance for the output file.
* @throws IOException if any.
* @see XmlStreamWriter
*/
public static XmlStreamWriter newXmlWriter( File file )
throws IOException
{
return new XmlStreamWriter( file );
}
/**
* Create a new Writer with default plaform encoding.
*
* @param out not null output stream.
* @return a writer instance for the output stream using the default platform charset.
* @throws IOException if any.
* @see Charset#defaultCharset()
*/
public static Writer newPlatformWriter( OutputStream out )
{
return new OutputStreamWriter( out );
}
/**
* Create a new Writer with default plaform encoding.
*
* @param file not null file.
* @return a writer instance for the output file using the default platform charset.
* @throws IOException if any.
* @see Charset#defaultCharset()
*/
public static Writer newPlatformWriter( File file )
throws IOException
{
return new FileWriter( file );
}
/**
* Create a new Writer with specified encoding.
*
* @param out not null output stream.
* @param encoding not null supported encoding.
* @return a writer instance for the output stream using the given encoding.
* @throws UnsupportedEncodingException if any.
* @see Supported encodings
*/
public static Writer newWriter( OutputStream out, String encoding )
throws UnsupportedEncodingException
{
return new OutputStreamWriter( out, encoding );
}
/**
* Create a new Writer with specified encoding.
*
* @param file not null file.
* @param encoding not null supported encoding.
* @return a writer instance for the output file using the given encoding.
* @throws UnsupportedEncodingException if any.
* @throws FileNotFoundException if any.
* @see Supported encodings
*/
public static Writer newWriter( File file, String encoding )
throws UnsupportedEncodingException, FileNotFoundException
{
return newWriter( new FileOutputStream( file ), encoding );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/ 0000775 0000000 0000000 00000000000 11660305702 0024340 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/Arg.java 0000664 0000000 0000000 00000001462 11660305702 0025717 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.File;
public interface Arg
{
void setValue( String value );
void setLine( String line );
void setFile( File value );
String[] getParts();
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/CommandLineException.java 0000664 0000000 0000000 00000002050 11660305702 0031245 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
/**
* @author Trygve Laugstøl
* @version $Id: CommandLineException.java 8010 2009-01-07 12:59:50Z vsiveton $
*/
public class CommandLineException
extends Exception
{
public CommandLineException( String message )
{
super( message );
}
public CommandLineException( String message, Throwable cause )
{
super( message, cause );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/CommandLineTimeOutException.java 0000664 0000000 0000000 00000002576 11660305702 0032571 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli;
/*
* 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.
*/
/**
*
* @author olamy
* @since 1.5.9
* @version $Id: CommandLineTimeOutException.java 8153 2009-04-25 22:07:51Z olamy $
*/
public class CommandLineTimeOutException
extends CommandLineException
{
/**
* @param message
*/
public CommandLineTimeOutException( String message )
{
super( message );
}
/**
* @param message
* @param cause
*/
public CommandLineTimeOutException( String message, Throwable cause )
{
super( message, cause );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/CommandLineUtils.java 0000664 0000000 0000000 00000051526 11660305702 0030423 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import org.codehaus.plexus.util.Os;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;
/**
* @author Trygve Laugstøl
* @version $Id: CommandLineUtils.java 8700 2010-04-24 12:03:28Z bentmann $
*/
public abstract class CommandLineUtils
{
private static Map processes = Collections.synchronizedMap( new HashMap() );
private static Thread shutdownHook = new Thread( "CommandlineUtil shutdown" )
{
public void run()
{
if ( ( processes != null ) && ( processes.size() > 0 ) )
{
System.err.println( "Destroying " + processes.size() + " processes" );
for ( Iterator it = processes.values().iterator(); it.hasNext(); )
{
System.err.println( "Destroying process.." );
( (Process) it.next() ).destroy();
}
System.err.println( "Destroyed " + processes.size() + " processes" );
}
}
};
static
{
shutdownHook.setContextClassLoader( null );
addShutdownHook();
}
public static void addShutdownHook()
{
Runtime.getRuntime().addShutdownHook( shutdownHook );
}
public static void removeShutdownHook( boolean execute )
{
Runtime.getRuntime().removeShutdownHook( shutdownHook );
if ( execute )
{
shutdownHook.run();
}
}
public static class StringStreamConsumer
implements StreamConsumer
{
private StringBuffer string = new StringBuffer();
private String ls = System.getProperty( "line.separator" );
public void consumeLine( String line )
{
string.append( line ).append( ls );
}
public String getOutput()
{
return string.toString();
}
}
public static int executeCommandLine( Commandline cl, StreamConsumer systemOut, StreamConsumer systemErr )
throws CommandLineException
{
return executeCommandLine( cl, null, systemOut, systemErr, 0 );
}
public static int executeCommandLine( Commandline cl, StreamConsumer systemOut, StreamConsumer systemErr,
int timeoutInSeconds )
throws CommandLineException
{
return executeCommandLine( cl, null, systemOut, systemErr, timeoutInSeconds );
}
public static int executeCommandLine( Commandline cl, InputStream systemIn, StreamConsumer systemOut,
StreamConsumer systemErr )
throws CommandLineException
{
return executeCommandLine( cl, systemIn, systemOut, systemErr, 0 );
}
/**
* @param cl The command line to execute
* @param systemIn The input to read from, must be thread safe
* @param systemOut A consumer that receives output, must be thread safe
* @param systemErr A consumer that receives system error stream output, must be thread safe
* @param timeoutInSeconds Positive integer to specify timeout, zero and negative integers for no timeout.
* @return A return value, see {@link Process#exitValue()}
* @throws CommandLineException or CommandLineTimeOutException if time out occurs
*/
public static int executeCommandLine( Commandline cl, InputStream systemIn, StreamConsumer systemOut,
StreamConsumer systemErr, int timeoutInSeconds )
throws CommandLineException
{
if ( cl == null )
{
throw new IllegalArgumentException( "cl cannot be null." );
}
Process p;
p = cl.execute();
processes.put( new Long( cl.getPid() ), p );
StreamFeeder inputFeeder = null;
if ( systemIn != null )
{
inputFeeder = new StreamFeeder( systemIn, p.getOutputStream() );
}
StreamPumper outputPumper = new StreamPumper( p.getInputStream(), systemOut );
StreamPumper errorPumper = new StreamPumper( p.getErrorStream(), systemErr );
if ( inputFeeder != null )
{
inputFeeder.start();
}
outputPumper.start();
errorPumper.start();
try
{
int returnValue;
if ( timeoutInSeconds <= 0 )
{
returnValue = p.waitFor();
}
else
{
long now = System.currentTimeMillis();
long timeoutInMillis = 1000L * timeoutInSeconds;
long finish = now + timeoutInMillis;
while ( isAlive( p ) && ( System.currentTimeMillis() < finish ) )
{
Thread.sleep( 10 );
}
if ( isAlive( p ) )
{
throw new InterruptedException( "Process timeout out after " + timeoutInSeconds + " seconds" );
}
returnValue = p.exitValue();
}
if ( inputFeeder != null )
{
synchronized ( inputFeeder )
{
while ( !inputFeeder.isDone() )
{
inputFeeder.wait();
}
}
}
synchronized ( outputPumper )
{
while ( !outputPumper.isDone() )
{
outputPumper.wait();
}
}
synchronized ( errorPumper )
{
while ( !errorPumper.isDone() )
{
errorPumper.wait();
}
}
processes.remove( new Long( cl.getPid() ) );
if ( outputPumper.getException() != null )
{
throw new CommandLineException( "Error inside systemOut parser", outputPumper.getException() );
}
if ( errorPumper.getException() != null )
{
throw new CommandLineException( "Error inside systemErr parser", errorPumper.getException() );
}
return returnValue;
}
catch ( InterruptedException ex )
{
killProcess( cl.getPid() );
throw new CommandLineTimeOutException( "Error while executing external command, process killed.", ex );
}
finally
{
if ( inputFeeder != null )
{
inputFeeder.close();
}
outputPumper.close();
errorPumper.close();
p.destroy();
if ( processes.get( new Long( cl.getPid() ) ) != null )
{
processes.remove( new Long( cl.getPid() ) );
}
}
}
/**
* Gets the shell environment variables for this process. Note that the returned mapping from variable names to
* values will always be case-sensitive regardless of the platform, i.e. getSystemEnvVars().get("path")
* and getSystemEnvVars().get("PATH")
will in general return different values. However, on platforms
* with case-insensitive environment variables like Windows, all variable names will be normalized to upper case.
*
* @return The shell environment variables, can be empty but never null
.
* @throws IOException If the environment variables could not be queried from the shell.
* @see System#getenv() System.getenv() API, new in JDK 5.0, to get the same result
* since 2.0.2 System#getenv() will be used if available in the current running jvm.
*/
public static Properties getSystemEnvVars()
throws IOException
{
return getSystemEnvVars( !Os.isFamily( Os.FAMILY_WINDOWS ) );
}
/**
* Return the shell environment variables. If caseSensitive == true
, then envar
* keys will all be upper-case.
*
* @param caseSensitive Whether environment variable keys should be treated case-sensitively.
* @return Properties object of (possibly modified) envar keys mapped to their values.
* @throws IOException
* @see System#getenv() System.getenv() API, new in JDK 5.0, to get the same result
* since 2.0.2 System#getenv() will be used if available in the current running jvm.
*/
public static Properties getSystemEnvVars( boolean caseSensitive )
throws IOException
{
// check if it's 1.5+ run
Method getenvMethod = getEnvMethod();
if ( getenvMethod != null )
{
try
{
return getEnvFromSystem( getenvMethod, caseSensitive );
}
catch ( IllegalAccessException e )
{
throw new IOException( e.getMessage() );
}
catch ( IllegalArgumentException e )
{
throw new IOException( e.getMessage() );
}
catch ( InvocationTargetException e )
{
throw new IOException( e.getMessage() );
}
}
Process p = null;
try
{
Properties envVars = new Properties();
Runtime r = Runtime.getRuntime();
//If this is windows set the shell to command.com or cmd.exe with correct arguments.
boolean overriddenEncoding = false;
if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
{
if ( Os.isFamily( Os.FAMILY_WIN9X ) )
{
p = r.exec( "command.com /c set" );
}
else
{
overriddenEncoding = true;
// /U = change stdout encoding to UTF-16LE to avoid encoding inconsistency
// between command-line/DOS and GUI/Windows, see PLXUTILS-124
p = r.exec( "cmd.exe /U /c set" );
}
}
else
{
p = r.exec( "env" );
}
Reader reader = overriddenEncoding
? new InputStreamReader( p.getInputStream(), ReaderFactory.UTF_16LE )
: new InputStreamReader( p.getInputStream() );
BufferedReader br = new BufferedReader( reader );
String line;
String lastKey = null;
String lastVal = null;
while ( ( line = br.readLine() ) != null )
{
int idx = line.indexOf( '=' );
if ( idx > 0 )
{
lastKey = line.substring( 0, idx );
if ( !caseSensitive )
{
lastKey = lastKey.toUpperCase( Locale.ENGLISH );
}
lastVal = line.substring( idx + 1 );
envVars.setProperty( lastKey, lastVal );
}
else if ( lastKey != null )
{
lastVal += "\n" + line;
envVars.setProperty( lastKey, lastVal );
}
}
return envVars;
}
finally
{
if ( p != null )
{
p.destroy();
}
}
}
/**
* Kill a process launched by executeCommandLine methods.
* Doesn't work correctly on windows, only the cmd process will be destroy but not the sub process (Bug ID 4770092)
*
* @param pid The pid of command return by Commandline.getPid()
*/
public static void killProcess( long pid )
{
Process p = (Process) processes.get( new Long( pid ) );
if ( p != null )
{
p.destroy();
System.out.println( "Process " + pid + " is killed." );
processes.remove( new Long( pid ) );
}
else
{
System.out.println( "don't exist." );
}
}
public static boolean isAlive( long pid )
{
return ( processes.get( new Long( pid ) ) != null );
}
public static boolean isAlive( Process p )
{
if ( p == null )
{
return false;
}
try
{
p.exitValue();
return false;
}
catch ( IllegalThreadStateException e )
{
return true;
}
}
public static String[] translateCommandline( String toProcess )
throws Exception
{
if ( ( toProcess == null ) || ( toProcess.length() == 0 ) )
{
return new String[0];
}
// parse with a simple finite state machine
final int normal = 0;
final int inQuote = 1;
final int inDoubleQuote = 2;
int state = normal;
StringTokenizer tok = new StringTokenizer( toProcess, "\"\' ", true );
Vector v = new Vector();
StringBuffer current = new StringBuffer();
while ( tok.hasMoreTokens() )
{
String nextTok = tok.nextToken();
switch ( state )
{
case inQuote:
if ( "\'".equals( nextTok ) )
{
state = normal;
}
else
{
current.append( nextTok );
}
break;
case inDoubleQuote:
if ( "\"".equals( nextTok ) )
{
state = normal;
}
else
{
current.append( nextTok );
}
break;
default:
if ( "\'".equals( nextTok ) )
{
state = inQuote;
}
else if ( "\"".equals( nextTok ) )
{
state = inDoubleQuote;
}
else if ( " ".equals( nextTok ) )
{
if ( current.length() != 0 )
{
v.addElement( current.toString() );
current.setLength( 0 );
}
}
else
{
current.append( nextTok );
}
break;
}
}
if ( current.length() != 0 )
{
v.addElement( current.toString() );
}
if ( ( state == inQuote ) || ( state == inDoubleQuote ) )
{
throw new CommandLineException( "unbalanced quotes in " + toProcess );
}
String[] args = new String[v.size()];
v.copyInto( args );
return args;
}
/**
* Put quotes around the given String if necessary.
*If the argument doesn't include spaces or quotes, return it * as is. If it contains double quotes, use single quotes - else * surround the argument by double quotes.
* * @throws CommandLineException if the argument contains both, single * and double quotes. * @deprecated Use {@link StringUtils#quoteAndEscape(String, char, char[], char[], char, boolean)}, * {@link StringUtils#quoteAndEscape(String, char, char[], char, boolean)}, or * {@link StringUtils#quoteAndEscape(String, char)} instead. */ public static String quote( String argument ) throws CommandLineException { return quote( argument, false, false, true ); } /** *Put quotes around the given String if necessary.
*If the argument doesn't include spaces or quotes, return it * as is. If it contains double quotes, use single quotes - else * surround the argument by double quotes.
* * @throws CommandLineException if the argument contains both, single * and double quotes. * @deprecated Use {@link StringUtils#quoteAndEscape(String, char, char[], char[], char, boolean)}, * {@link StringUtils#quoteAndEscape(String, char, char[], char, boolean)}, or * {@link StringUtils#quoteAndEscape(String, char)} instead. */ public static String quote( String argument, boolean wrapExistingQuotes ) throws CommandLineException { return quote( argument, false, false, wrapExistingQuotes ); } /** * @deprecated Use {@link StringUtils#quoteAndEscape(String, char, char[], char[], char, boolean)}, * {@link StringUtils#quoteAndEscape(String, char, char[], char, boolean)}, or * {@link StringUtils#quoteAndEscape(String, char)} instead. */ public static String quote( String argument, boolean escapeSingleQuotes, boolean escapeDoubleQuotes, boolean wrapExistingQuotes ) throws CommandLineException { if ( argument.indexOf( "\"" ) > -1 ) { if ( argument.indexOf( "\'" ) > -1 ) { throw new CommandLineException( "Can't handle single and double quotes in same argument" ); } else { if ( escapeSingleQuotes ) { return "\\\'" + argument + "\\\'"; } else if ( wrapExistingQuotes ) { return '\'' + argument + '\''; } } } else if ( argument.indexOf( "\'" ) > -1 ) { if ( escapeDoubleQuotes ) { return "\\\"" + argument + "\\\""; } else if ( wrapExistingQuotes ) { return '\"' + argument + '\"'; } } else if ( argument.indexOf( " " ) > -1 ) { if ( escapeDoubleQuotes ) { return "\\\"" + argument + "\\\""; } else { return '\"' + argument + '\"'; } } return argument; } public static String toString( String[] line ) { // empty path return empty string if ( ( line == null ) || ( line.length == 0 ) ) { return ""; } // path containing one or more elements final StringBuffer result = new StringBuffer(); for ( int i = 0; i < line.length; i++ ) { if ( i > 0 ) { result.append( ' ' ); } try { result.append( StringUtils.quoteAndEscape( line[i], '\"' ) ); } catch ( Exception e ) { System.err.println( "Error quoting argument: " + e.getMessage() ); } } return result.toString(); } private static Method getEnvMethod() { try { return System.class.getMethod( "getenv", null ); } catch ( NoSuchMethodException e ) { return null; } catch ( SecurityException e ) { return null; } } private static Properties getEnvFromSystem( Method method, boolean caseSensitive ) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { Properties envVars = new Properties(); Map envs = (Map) method.invoke( null, null ); Iterator iterator = envs.keySet().iterator(); while ( iterator.hasNext() ) { String key = (String) iterator.next(); String value = (String) envs.get( key ); if ( !caseSensitive ) { key = key.toUpperCase( Locale.ENGLISH ); } envVars.put( key, value ); } return envVars; } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/Commandline.java 0000664 0000000 0000000 00000053177 11660305702 0027446 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli; /* * Copyright The Codehaus Foundation. * * 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. */ /*************************************************************************************************** * CruiseControl, a Continuous Integration Toolkit Copyright (c) 2001-2003, ThoughtWorks, Inc. 651 W * Washington Ave. Suite 500 Chicago, IL 60661 USA 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. + * Neither the name of ThoughtWorks, Inc., CruiseControl, nor the names of its contributors may be * used to endorse or promote products derived from this software without specific prior written * permission. * * 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 REGENTS 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. **************************************************************************************************/ /* * ==================================================================== * Copyright 2003-2004 The Apache Software Foundation. * * 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. * ==================================================================== */ import org.codehaus.plexus.util.Os; import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.cli.shell.BourneShell; import org.codehaus.plexus.util.cli.shell.CmdShell; import org.codehaus.plexus.util.cli.shell.CommandShell; import org.codehaus.plexus.util.cli.shell.Shell; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; import java.util.Vector; /** * * Commandline objects help handling command lines specifying processes to * execute. * * * The class can be used to define a command line as nested elements or as a * helper to define a command line by an application. * * *
* <someelement>
* <acommandline executable="/executable/to/run">
* <argument value="argument 1" />
* <argument line="argument_1 argument_2 argument_3" />
* <argument value="argument 4" />
* </acommandline>
* </someelement>
*
*
*
* The element someelement
must provide a method
* createAcommandline
which returns an instance of this class.
*
*
* @author thomas.haas@softwired-inc.com
* @author Stefan Bodewig
*/
public class Commandline
implements Cloneable
{
/**
* @deprecated Use {@link org.codehaus.plexus.util.Os} class instead.
*/
protected static final String OS_NAME = "os.name";
/**
* @deprecated Use {@link org.codehaus.plexus.util.Os} class instead.
*/
protected static final String WINDOWS = "Windows";
protected Vector arguments = new Vector();
//protected Vector envVars = new Vector();
// synchronized added to preserve synchronize of Vector class
protected Map envVars = Collections.synchronizedMap( new LinkedHashMap() );
private long pid = -1;
private Shell shell;
/**
* @deprecated Use {@link Commandline#setExecutable(String)} instead.
*/
protected String executable;
/**
* @deprecated Use {@link Commandline#setWorkingDirectory(File)} or
* {@link Commandline#setWorkingDirectory(String)} instead.
*/
private File workingDir;
/**
* Create a new command line object.
* Shell is autodetected from operating system
*
* @param toProcess
*/
public Commandline( String toProcess, Shell shell )
{
this.shell = shell;
String[] tmp = new String[0];
try
{
tmp = CommandLineUtils.translateCommandline( toProcess );
}
catch ( Exception e )
{
System.err.println( "Error translating Commandline." );
}
if ( ( tmp != null ) && ( tmp.length > 0 ) )
{
setExecutable( tmp[0] );
for ( int i = 1; i < tmp.length; i++ )
{
createArgument().setValue( tmp[i] );
}
}
}
/**
* Create a new command line object.
* Shell is autodetected from operating system
*/
public Commandline( Shell shell )
{
this.shell = shell;
}
/**
* Create a new command line object.
* Shell is autodetected from operating system
*
* @param toProcess
*/
public Commandline( String toProcess )
{
setDefaultShell();
String[] tmp = new String[0];
try
{
tmp = CommandLineUtils.translateCommandline( toProcess );
}
catch ( Exception e )
{
System.err.println( "Error translating Commandline." );
}
if ( ( tmp != null ) && ( tmp.length > 0 ) )
{
setExecutable( tmp[0] );
for ( int i = 1; i < tmp.length; i++ )
{
createArgument().setValue( tmp[i] );
}
}
}
/**
* Create a new command line object.
* Shell is autodetected from operating system
*/
public Commandline()
{
setDefaultShell();
}
public long getPid()
{
if ( pid == -1 )
{
pid = Long.parseLong( String.valueOf( System.currentTimeMillis() ) );
}
return pid;
}
public void setPid( long pid )
{
this.pid = pid;
}
/**
* Class to keep track of the position of an Argument.
*/
// This class is there to support the srcfile and targetfile // elements of <execon> and <transform> - don't know // whether there might be additional use cases.
--SB public class Marker { private int position; private int realPos = -1; Marker( int position ) { this.position = position; } /** * Return the number of arguments that preceeded this marker. * *The name of the executable - if set - is counted as the * very first argument.
*/ public int getPosition() { if ( realPos == -1 ) { realPos = ( getExecutable() == null ? 0 : 1 ); for ( int i = 0; i < position; i++ ) { Arg arg = (Arg) arguments.elementAt( i ); realPos += arg.getParts().length; } } return realPos; } } /** *Sets the shell or command-line interpretor for the detected operating system, * and the shell arguments.
*/ private void setDefaultShell() { //If this is windows set the shell to command.com or cmd.exe with correct arguments. if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) { if ( Os.isFamily( Os.FAMILY_WIN9X ) ) { setShell( new CommandShell() ); } else { setShell( new CmdShell() ); } } else { setShell( new BourneShell() ); } } /** * Creates an argument object. * *Each commandline object has at most one instance of the
* argument class. This method calls
* this.createArgument(false)
.
Each commandline object has at most one instance of the * argument class.
* * @param insertAtStart if true, the argument is inserted at the * beginning of the list of args, otherwise it is appended. * @deprecated Use {@link Commandline#createArg(boolean)} instead */ public Argument createArgument( boolean insertAtStart ) { Argument argument = new Argument(); if ( insertAtStart ) { arguments.insertElementAt( argument, 0 ); } else { arguments.addElement( argument ); } return argument; } /** * Creates an argument object. * *Each commandline object has at most one instance of the
* argument class. This method calls
* this.createArgument(false)
.
Each commandline object has at most one instance of the * argument class.
* * @param insertAtStart if true, the argument is inserted at the * beginning of the list of args, otherwise it is appended. */ public Arg createArg( boolean insertAtStart ) { Arg argument = new Argument(); if ( insertAtStart ) { arguments.insertElementAt( argument, 0 ); } else { arguments.addElement( argument ); } return argument; } /** * Adds an argument object to our list of args. * * @return the argument object. * @see #addArg(Arg,boolean) */ public void addArg( Arg argument ) { this.addArg( argument, false ); } /** * Adds an argument object to our list of args. * * @param insertAtStart if true, the argument is inserted at the * beginning of the list of args, otherwise it is appended. */ public void addArg( Arg argument, boolean insertAtStart ) { if ( insertAtStart ) { arguments.insertElementAt( argument, 0 ); } else { arguments.addElement( argument ); } } /** * Sets the executable to run. */ public void setExecutable( String executable ) { shell.setExecutable( executable ); this.executable = executable; } public String getExecutable() { String exec = shell.getExecutable(); if ( exec == null ) { exec = executable; } return exec; } public void addArguments( String[] line ) { for ( int i = 0; i < line.length; i++ ) { createArgument().setValue( line[i] ); } } /** * Add an environment variable */ public void addEnvironment( String name, String value ) { //envVars.add( name + "=" + value ); envVars.put( name, value ); } /** * Add system environment variables */ public void addSystemEnvironment() throws Exception { Properties systemEnvVars = CommandLineUtils.getSystemEnvVars(); for ( Iterator i = systemEnvVars.keySet().iterator(); i.hasNext(); ) { String key = (String) i.next(); if ( !envVars.containsKey( key ) ) { addEnvironment( key, systemEnvVars.getProperty( key ) ); } } } /** * Return the list of environment variables */ public String[] getEnvironmentVariables() throws CommandLineException { try { addSystemEnvironment(); } catch ( Exception e ) { throw new CommandLineException( "Error setting up environmental variables", e ); } String[] environmentVars = new String[envVars.size()]; int i = 0; for ( Iterator iterator = envVars.keySet().iterator(); iterator.hasNext(); ) { String name = (String) iterator.next(); String value = (String) envVars.get( name ); environmentVars[i] = name + "=" + value; i++; } return environmentVars; } /** * Returns the executable and all defined arguments. */ public String[] getCommandline() { final String[] args = getArguments(); String executable = getExecutable(); if ( executable == null ) { return args; } final String[] result = new String[args.length + 1]; result[0] = executable; System.arraycopy( args, 0, result, 1, args.length ); return result; } /** * Returns the shell, executable and all defined arguments. */ public String[] getShellCommandline() { // TODO: Provided only for backward compat. with <= 1.4 verifyShellState(); return (String[]) getShell().getShellCommandLine( getArguments() ).toArray( new String[0] ); } /** * Returns all arguments defined byaddLine
,
* addValue
or the argument object.
*/
public String[] getArguments()
{
Vector result = new Vector( arguments.size() * 2 );
for ( int i = 0; i < arguments.size(); i++ )
{
Argument arg = (Argument) arguments.elementAt( i );
String[] s = arg.getParts();
if ( s != null )
{
for ( int j = 0; j < s.length; j++ )
{
result.addElement( s[j] );
}
}
}
String[] res = new String[result.size()];
result.copyInto( res );
return res;
}
public String toString()
{
return StringUtils.join( getShellCommandline(), " " );
}
public int size()
{
return getCommandline().length;
}
public Object clone()
{
Commandline c = new Commandline( (Shell) shell.clone() );
c.executable = executable;
c.workingDir = workingDir;
c.addArguments( getArguments() );
return c;
}
/**
* Clear out the whole command line.
*/
public void clear()
{
executable = null;
workingDir = null;
shell.setExecutable( null );
shell.clearArguments();
arguments.removeAllElements();
}
/**
* Clear out the arguments but leave the executable in place for another operation.
*/
public void clearArgs()
{
arguments.removeAllElements();
}
/**
* Return a marker.
*
* This marker can be used to locate a position on the * commandline - to insert something for example - when all * parameters have been set.
*/ public Marker createMarker() { return new Marker( arguments.size() ); } /** * Sets execution directory. */ public void setWorkingDirectory( String path ) { shell.setWorkingDirectory( path ); workingDir = new File( path ); } /** * Sets execution directory. */ public void setWorkingDirectory( File workingDirectory ) { shell.setWorkingDirectory( workingDirectory ); workingDir = workingDirectory; } public File getWorkingDirectory() { File workDir = shell.getWorkingDirectory(); if ( workDir == null ) { workDir = workingDir; } return workDir; } /** * Executes the command. */ public Process execute() throws CommandLineException { // TODO: Provided only for backward compat. with <= 1.4 verifyShellState(); Process process; //addEnvironment( "MAVEN_TEST_ENVAR", "MAVEN_TEST_ENVAR_VALUE" ); String[] environment = getEnvironmentVariables(); File workingDir = shell.getWorkingDirectory(); try { if ( workingDir == null ) { process = Runtime.getRuntime().exec( getShellCommandline(), environment ); } else { if ( !workingDir.exists() ) { throw new CommandLineException( "Working directory \"" + workingDir.getPath() + "\" does not exist!" ); } else if ( !workingDir.isDirectory() ) { throw new CommandLineException( "Path \"" + workingDir.getPath() + "\" does not specify a directory." ); } process = Runtime.getRuntime().exec( getShellCommandline(), environment, workingDir ); } } catch ( IOException ex ) { throw new CommandLineException( "Error while executing process.", ex ); } return process; } /** * @deprecated Remove once backward compat with plexus-utils <= 1.4 is no longer a consideration */ private void verifyShellState() { if ( shell.getWorkingDirectory() == null ) { shell.setWorkingDirectory( workingDir ); } if ( shell.getExecutable() == null ) { shell.setExecutable( executable ); } } public Properties getSystemEnvVars() throws Exception { return CommandLineUtils.getSystemEnvVars(); } /** * Allows to set the shell to be used in this command line. * * @param shell * @since 1.2 */ public void setShell( Shell shell ) { this.shell = shell; } /** * Get the shell to be used in this command line. * * @since 1.2 */ public Shell getShell() { return shell; } /** * @deprecated Use {@link CommandLineUtils#translateCommandline(String)} instead. */ public static String[] translateCommandline( String toProcess ) throws Exception { return CommandLineUtils.translateCommandline( toProcess ); } /** * @deprecated Use {@link CommandLineUtils#quote(String)} instead. */ public static String quoteArgument( String argument ) throws CommandLineException { return CommandLineUtils.quote( argument ); } /** * @deprecated Use {@link CommandLineUtils#toString(String[])} instead. */ public static String toString( String[] line ) { return CommandLineUtils.toString( line ); } public static class Argument implements Arg { private String[] parts; /* (non-Javadoc) * @see org.codehaus.plexus.util.cli.Argumnt#setValue(java.lang.String) */ public void setValue( String value ) { if ( value != null ) { parts = new String[] { value }; } } /* (non-Javadoc) * @see org.codehaus.plexus.util.cli.Argumnt#setLine(java.lang.String) */ public void setLine( String line ) { if ( line == null ) { return; } try { parts = CommandLineUtils.translateCommandline( line ); } catch ( Exception e ) { System.err.println( "Error translating Commandline." ); } } /* (non-Javadoc) * @see org.codehaus.plexus.util.cli.Argumnt#setFile(java.io.File) */ public void setFile( File value ) { parts = new String[] { value.getAbsolutePath() }; } /* (non-Javadoc) * @see org.codehaus.plexus.util.cli.Argumnt#getParts() */ public String[] getParts() { return parts; } } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/DefaultConsumer.java 0000664 0000000 0000000 00000001665 11660305702 0030313 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli; /* * Copyright The Codehaus Foundation. * * 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. */ /** * @author Emmanuel Venisse * @version $Id: DefaultConsumer.java 8010 2009-01-07 12:59:50Z vsiveton $ */ public class DefaultConsumer implements StreamConsumer { public void consumeLine( String line ) { System.out.println( line ); } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/EnhancedStringTokenizer.java 0000664 0000000 0000000 00000006324 11660305702 0031777 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli; /* * Copyright The Codehaus Foundation. * * 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. */ import java.util.StringTokenizer; /** * The java.util.StringTokenizer is horribly broken. * Given the string 1,,,3,,4 (, delim) * It will return 1,3,4 * Which is clearly wrong - 1,EMPTY,EMPTY,3,EMPTY,4 is what it should return * @version $Id: EnhancedStringTokenizer.java 8010 2009-01-07 12:59:50Z vsiveton $ */ public final class EnhancedStringTokenizer { private StringTokenizer cst = null; String cdelim; final boolean cdelimSingleChar; final char cdelimChar; boolean creturnDelims; String lastToken = null; boolean delimLast = true; public EnhancedStringTokenizer( String str ) { this( str, " \t\n\r\f", false ); } public EnhancedStringTokenizer( String str, String delim ) { this( str, delim, false ); } public EnhancedStringTokenizer( String str, String delim, boolean returnDelims ) { cst = new StringTokenizer( str, delim, true ); cdelim = delim; creturnDelims = returnDelims; cdelimSingleChar = ( delim.length() == 1 ); cdelimChar = delim.charAt( 0 ); } public boolean hasMoreTokens() { return cst.hasMoreTokens(); } private String internalNextToken() { if ( lastToken != null ) { String last = lastToken; lastToken = null; return last; } String token = cst.nextToken(); if ( isDelim( token ) ) { if ( delimLast ) { lastToken = token; return ""; } else { delimLast = true; return token; } } else { delimLast = false; return token; } } public String nextToken() { String token = internalNextToken(); if ( creturnDelims ) { return token; } if ( isDelim( token ) ) { return hasMoreTokens() ? internalNextToken() : ""; } else { return token; } } private boolean isDelim( String str ) { if ( str.length() == 1 ) { char ch = str.charAt( 0 ); if ( cdelimSingleChar ) { if ( cdelimChar == ch ) { return true; } } else { if ( cdelim.indexOf( ch ) >= 0 ) { return true; } } } return false; } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/StreamConsumer.java 0000664 0000000 0000000 00000006175 11660305702 0030163 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli; /* * Copyright The Codehaus Foundation. * * 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. */ /******************************************************************************** * CruiseControl, a Continuous Integration Toolkit * Copyright (c) 2003, ThoughtWorks, Inc. * 651 W Washington Ave. Suite 500 * Chicago, IL 60661 USA * 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. * * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * 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 REGENTS 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. ********************************************************************************/ /** * Works in concert with the StreamPumper class to * allow implementations to gain access to the lines being * "Pumped". * * Please note that implementations of this interface can be expected to be * called from arbitrary threads and must therefore be threadsafe. * * @author Florin Vancea * @author Paul Julius * @version $Id: StreamConsumer.java 8695 2010-04-20 18:37:36Z bentmann $ */ public interface StreamConsumer { /** * Called when the StreamPumper pumps a line from the Stream. */ public void consumeLine( String line ); } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/StreamFeeder.java 0000664 0000000 0000000 00000006457 11660305702 0027565 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli; /* * Copyright The Codehaus Foundation. * * 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. */ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Read from an InputStream and write the output to an OutputStream. * * @author Trygve Laugstøl * @version $Id: StreamFeeder.java 8695 2010-04-20 18:37:36Z bentmann $ */ public class StreamFeeder extends Thread { private InputStream input; private OutputStream output; private boolean done; /** * Create a new StreamFeeder * * @param input Stream to read from * @param output Stream to write to */ public StreamFeeder( InputStream input, OutputStream output ) { this.input = input; this.output = output; } // ---------------------------------------------------------------------- // Runnable implementation // ---------------------------------------------------------------------- public void run() { try { feed(); } catch ( Throwable ex ) { // Catched everything so the streams will be closed and flagged as done. } finally { close(); synchronized ( this ) { done = true; this.notifyAll(); } } } // ---------------------------------------------------------------------- // // ---------------------------------------------------------------------- public void close() { if ( input != null ) { synchronized ( input ) { try { input.close(); } catch ( IOException ex ) { // ignore } input = null; } } if ( output != null ) { synchronized ( output ) { try { output.close(); } catch ( IOException ex ) { // ignore } output = null; } } } public boolean isDone() { return done; } // ---------------------------------------------------------------------- // // ---------------------------------------------------------------------- private void feed() throws IOException { int data = input.read(); while ( !done && data != -1 ) { synchronized ( output ) { output.write( data ); data = input.read(); } } } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/StreamPumper.java 0000664 0000000 0000000 00000013636 11660305702 0027640 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli; /* * Copyright The Codehaus Foundation. * * 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. */ /******************************************************************************** * CruiseControl, a Continuous Integration Toolkit * Copyright (c) 2001-2003, ThoughtWorks, Inc. * 651 W Washington Ave. Suite 500 * Chicago, IL 60661 USA * 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. * * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * 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 REGENTS 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. ********************************************************************************/ /* ==================================================================== * Copyright 2003-2004 The Apache Software Foundation. * * 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. * ==================================================================== */ import org.codehaus.plexus.util.IOUtil; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; /** * Class to pump the error stream during Process's runtime. Copied from the Ant * built-in task. * * @author Florin Vancea * @author Paul Julius * @since June 11, 2001 * @version $Id: StreamPumper.java 8701 2010-04-24 12:10:17Z bentmann $ */ public class StreamPumper extends Thread { private final BufferedReader in; private final StreamConsumer consumer; private final PrintWriter out; private volatile Exception exception = null; private static final int SIZE = 1024; boolean done; public StreamPumper( InputStream in ) { this( in, (StreamConsumer) null ); } public StreamPumper( InputStream in, StreamConsumer consumer ) { this( in, null, consumer ); } public StreamPumper( InputStream in, PrintWriter writer ) { this( in, writer, null ); } public StreamPumper( InputStream in, PrintWriter writer, StreamConsumer consumer ) { this.in = new BufferedReader( new InputStreamReader( in ), SIZE ); this.out = writer; this.consumer = consumer; } public void run() { try { for ( String line = in.readLine(); line != null; line = in.readLine() ) { try { if ( exception == null ) { consumeLine( line ); } } catch ( Exception t ) { exception = t; } if ( out != null ) { out.println( line ); out.flush(); } } } catch ( IOException e ) { exception = e; } finally { IOUtil.close( in ); synchronized ( this ) { done = true; this.notifyAll(); } } } public void flush() { if ( out != null ) { out.flush(); } } public void close() { IOUtil.close( out ); } public boolean isDone() { return done; } public Exception getException() { return exception; } private void consumeLine( String line ) { if ( consumer != null ) { consumer.consumeLine( line ); } } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/WriterStreamConsumer.java 0000664 0000000 0000000 00000002221 11660305702 0031344 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli; /* * Copyright The Codehaus Foundation. * * 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. */ import java.io.PrintWriter; import java.io.Writer; /** * @author Jason van Zyl * @version $Id: WriterStreamConsumer.java 8010 2009-01-07 12:59:50Z vsiveton $ */ public class WriterStreamConsumer implements StreamConsumer { private PrintWriter writer; public WriterStreamConsumer( Writer writer ) { this.writer = new PrintWriter( writer ); } public void consumeLine( String line ) { writer.println( line ); writer.flush(); } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/shell/ 0000775 0000000 0000000 00000000000 11660305702 0025447 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/shell/BourneShell.java 0000664 0000000 0000000 00000010725 11660305702 0030541 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli.shell; /* * Copyright The Codehaus Foundation. * * 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. */ import org.codehaus.plexus.util.Os; import org.codehaus.plexus.util.StringUtils; import java.util.ArrayList; import java.util.List; /** * @author Jason van Zyl * @version $Id: BourneShell.java 8010 2009-01-07 12:59:50Z vsiveton $ */ public class BourneShell extends Shell { private static final char[] BASH_QUOTING_TRIGGER_CHARS = { ' ', '$', ';', '&', '|', '<', '>', '*', '?', '(', ')', '[', ']', '{', '}', '`' }; public BourneShell() { this( false ); } public BourneShell( boolean isLoginShell ) { setShellCommand( "/bin/sh" ); setArgumentQuoteDelimiter( '\'' ); setExecutableQuoteDelimiter( '\"' ); setSingleQuotedArgumentEscaped( true ); setSingleQuotedExecutableEscaped( false ); setQuotedExecutableEnabled( true ); if ( isLoginShell ) { addShellArg( "-l" ); } } /** {@inheritDoc} */ public String getExecutable() { if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) { return super.getExecutable(); } return unifyQuotes( super.getExecutable()); } public List getShellArgsList() { List shellArgs = new ArrayList(); List existingShellArgs = super.getShellArgsList(); if ( ( existingShellArgs != null ) && !existingShellArgs.isEmpty() ) { shellArgs.addAll( existingShellArgs ); } shellArgs.add( "-c" ); return shellArgs; } public String[] getShellArgs() { String[] shellArgs = super.getShellArgs(); if ( shellArgs == null ) { shellArgs = new String[0]; } if ( ( shellArgs.length > 0 ) && !shellArgs[shellArgs.length - 1].equals( "-c" ) ) { String[] newArgs = new String[shellArgs.length + 1]; System.arraycopy( shellArgs, 0, newArgs, 0, shellArgs.length ); newArgs[shellArgs.length] = "-c"; shellArgs = newArgs; } return shellArgs; } protected String getExecutionPreamble() { if ( getWorkingDirectoryAsString() == null ) { return null; } String dir = getWorkingDirectoryAsString(); StringBuffer sb = new StringBuffer(); sb.append( "cd " ); sb.append( unifyQuotes( dir ) ); sb.append( " && " ); return sb.toString(); } protected char[] getQuotingTriggerChars() { return BASH_QUOTING_TRIGGER_CHARS; } /** *Unify quotes in a path for the Bourne Shell.
* ** BourneShell.unifyQuotes(null) = null * BourneShell.unifyQuotes("") = (empty) * BourneShell.unifyQuotes("/test/quotedpath'abc") = /test/quotedpath\'abc * BourneShell.unifyQuotes("/test/quoted path'abc") = "/test/quoted path'abc" * BourneShell.unifyQuotes("/test/quotedpath\"abc") = "/test/quotedpath\"abc" * BourneShell.unifyQuotes("/test/quoted path\"abc") = "/test/quoted path\"abc" * BourneShell.unifyQuotes("/test/quotedpath\"'abc") = "/test/quotedpath\"'abc" * BourneShell.unifyQuotes("/test/quoted path\"'abc") = "/test/quoted path\"'abc" ** * @param path not null path. * @return the path unified correctly for the Bourne shell. */ protected static String unifyQuotes( String path ) { if ( path == null ) { return null; } if ( path.indexOf( " " ) == -1 && path.indexOf( "'" ) != -1 && path.indexOf( "\"" ) == -1 ) { return StringUtils.escape( path ); } return StringUtils.quoteAndEscape( path, '\"', BASH_QUOTING_TRIGGER_CHARS ); } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/shell/CmdShell.java 0000664 0000000 0000000 00000005703 11660305702 0030012 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli.shell; /* * Copyright The Codehaus Foundation. * * 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. */ import java.util.Arrays; import java.util.List; /** *
* Implementation to call the CMD Shell present on Windows NT, 2000 and XP *
* * @author Carlos Sanchez * @since 1.2 * @version $Id: CmdShell.java 8010 2009-01-07 12:59:50Z vsiveton $ */ public class CmdShell extends Shell { public CmdShell() { setShellCommand( "cmd.exe" ); setQuotedExecutableEnabled( true ); setShellArgs( new String[]{"/X", "/C"} ); } /** ** Specific implementation that quotes all the command line. *
** Workaround for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468220 *
** From cmd.exe /? output: *
* ** If /C or /K is specified, then the remainder of the command line after * the switch is processed as a command line, where the following logic is * used to process quote (") characters: * * 1. If all of the following conditions are met, then quote characters * on the command line are preserved: * * - no /S switch * - exactly two quote characters * - no special characters between the two quote characters, * where special is one of: &<>()@ˆ| * - there are one or more whitespace characters between the * the two quote characters * - the string between the two quote characters is the name * of an executable file. * * 2. Otherwise, old behavior is to see if the first character is * a quote character and if so, strip the leading character and * remove the last quote character on the command line, preserving * any text after the last quote character. ** *
* Always quoting the entire command line, regardless of these conditions * appears to make Windows processes invoke successfully. *
*/ public List getCommandLine( String executable, String[] arguments ) { StringBuffer sb = new StringBuffer(); sb.append( "\"" ); sb.append( super.getCommandLine( executable, arguments ).get( 0 ) ); sb.append( "\"" ); return Arrays.asList( new String[] { sb.toString() } ); } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/shell/CommandShell.java 0000664 0000000 0000000 00000002070 11660305702 0030657 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli.shell; /* * Copyright The Codehaus Foundation. * * 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. */ /** ** Implementation to call the Command.com Shell present on Windows 95, 98 and Me *
* * @author Carlos Sanchez * @since 1.2 * @version $Id: CommandShell.java 8010 2009-01-07 12:59:50Z vsiveton $ */ public class CommandShell extends Shell { public CommandShell() { setShellCommand( "command.com" ); setShellArgs( new String[]{"/C"} ); } } plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/cli/shell/Shell.java 0000664 0000000 0000000 00000024011 11660305702 0027357 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.cli.shell; /* * Copyright The Codehaus Foundation. * * 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. */ import org.codehaus.plexus.util.StringUtils; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** ** Class that abstracts the Shell functionality, * with subclases for shells that behave particularly, like *
command.com
cmd.exe
true if this vertex is connected with other vertex,false
otherwise
*/
public boolean isConnected( final String label )
{
final Vertex vertex = getVertex( label );
final boolean retValue = vertex.isConnected();
return retValue;
}
/**
* Return the list of labels of successor in order decided by topological sort
*
* @param label The label of the vertex whose predessors are serched
*
* @return The list of labels. Returned list contains also
* the label passed as parameter to this method. This label should
* always be the last item in the list.
*/
public List getSuccessorLabels( final String label )
{
final Vertex vertex = getVertex( label );
final List retValue;
//optimization.
if ( vertex.isLeaf() )
{
retValue = new ArrayList( 1 );
retValue.add( label );
}
else
{
retValue = TopologicalSorter.sort( vertex );
}
return retValue;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/dag/TopologicalSorter.java 0000664 0000000 0000000 00000006312 11660305702 0030644 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.dag;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @author Michal Maczka
* @version $Id: TopologicalSorter.java 8010 2009-01-07 12:59:50Z vsiveton $
*/
public class TopologicalSorter
{
private final static Integer NOT_VISTITED = new Integer( 0 );
private final static Integer VISITING = new Integer( 1 );
private final static Integer VISITED = new Integer( 2 );
/**
* @param graph
* @return List of String (vertex labels)
*/
public static List sort( final DAG graph )
{
return dfs( graph );
}
public static List sort( final Vertex vertex )
{
// we need to use addFirst method so we will use LinkedList explicitly
final LinkedList retValue = new LinkedList();
final Map vertexStateMap = new HashMap();
dfsVisit( vertex, vertexStateMap, retValue );
return retValue;
}
private static List dfs( final DAG graph )
{
final List verticies = graph.getVerticies();
// we need to use addFirst method so we will use LinkedList explicitly
final LinkedList retValue = new LinkedList();
final Map vertexStateMap = new HashMap();
for ( final Iterator iter = verticies.iterator(); iter.hasNext(); )
{
final Vertex vertex = ( Vertex ) iter.next();
if ( isNotVisited( vertex, vertexStateMap ) )
{
dfsVisit( vertex, vertexStateMap, retValue );
}
}
return retValue;
}
/**
* @param vertex
* @param vertexStateMap
* @return
*/
private static boolean isNotVisited( final Vertex vertex, final Map vertexStateMap )
{
if ( !vertexStateMap.containsKey( vertex ) )
{
return true;
}
final Integer state = ( Integer ) vertexStateMap.get( vertex );
return NOT_VISTITED.equals( state );
}
private static void dfsVisit( final Vertex vertex, final Map vertexStateMap, final LinkedList list )
{
vertexStateMap.put( vertex, VISITING );
final List verticies = vertex.getChildren();
for ( final Iterator iter = verticies.iterator(); iter.hasNext(); )
{
final Vertex v = ( Vertex ) iter.next();
if ( isNotVisited( v, vertexStateMap ) )
{
dfsVisit( v, vertexStateMap, list );
}
}
vertexStateMap.put( vertex, VISITED );
list.add( vertex.getLabel() );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/dag/Vertex.java 0000664 0000000 0000000 00000010625 11660305702 0026450 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.dag;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author Michal Maczka
* @version $Id: Vertex.java 8010 2009-01-07 12:59:50Z vsiveton $
*/
public class Vertex implements Cloneable, Serializable
{
//------------------------------------------------------------
//Fields
//------------------------------------------------------------
private String label = null;
List children = new ArrayList();
List parents = new ArrayList();
// ------------------------------------------------------------
// Constructors
// ------------------------------------------------------------
/**
*
*/
public Vertex( final String label )
{
this.label = label;
}
// ------------------------------------------------------------
// Accessors
// ------------------------------------------------------------
/**
* @return
*/
public String getLabel()
{
return label;
}
/**
* @param vertex
*/
public void addEdgeTo( final Vertex vertex )
{
children.add( vertex );
}
/**
* @param vertex
*/
public void removeEdgeTo( final Vertex vertex )
{
children.remove( vertex );
}
/**
* @param vertex
*/
public void addEdgeFrom( final Vertex vertex )
{
parents.add( vertex );
}
public void removeEdgeFrom( final Vertex vertex )
{
parents.remove( vertex );
}
public List getChildren()
{
return children;
}
/**
* Get the labels used by the most direct children.
*
* @return the labels used by the most direct children.
*/
public List getChildLabels()
{
final List retValue = new ArrayList( children.size() );
for ( final Iterator iter = children.iterator(); iter.hasNext(); )
{
final Vertex vertex = ( Vertex ) iter.next();
retValue.add( vertex.getLabel() );
}
return retValue;
}
/**
* Get the list the most direct ancestors (parents).
*
* @return list of parents
*/
public List getParents()
{
return parents;
}
/**
* Get the labels used by the most direct ancestors (parents).
*
* @return the labels used parents
*/
public List getParentLabels()
{
final List retValue = new ArrayList( parents.size() );
for ( final Iterator iter = parents.iterator(); iter.hasNext(); )
{
final Vertex vertex = ( Vertex ) iter.next();
retValue.add( vertex.getLabel() );
}
return retValue;
}
/**
* Indicates if given vertex has no child
*
* @return true if this vertex has no child, false
otherwise
*/
public boolean isLeaf()
{
return children.size() == 0;
}
/**
* Indicates if given vertex has no parent
*
* @return true if this vertex has no parent, false
otherwise
*/
public boolean isRoot()
{
return parents.size() == 0;
}
/**
* Indicates if there is at least one edee leading to or from given vertex
*
* @return true if this vertex is connected with other vertex,false
otherwise
*/
public boolean isConnected()
{
return isRoot() || isLeaf();
}
public Object clone() throws CloneNotSupportedException
{
// this is what's failing..
final Object retValue = super.clone();
return retValue;
}
public String toString()
{
return "Vertex{" +
"label='" + label + "'" +
"}";
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/introspection/ 0000775 0000000 0000000 00000000000 11660305702 0026471 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/introspection/ClassMap.java 0000664 0000000 0000000 00000035622 11660305702 0031047 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.introspection;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Hashtable;
import java.util.Map;
/**
* A cache of introspection information for a specific class instance.
* Keys {@link java.lang.Method} objects by a concatenation of the
* method name and the names of classes that make up the parameters.
*
* @author Jason van Zyl
* @author Bob McWhirter
* @author Attila Szegedi
* @author Geir Magnusson Jr.
* @version $Id: ClassMap.java 8010 2009-01-07 12:59:50Z vsiveton $
*/
public class ClassMap
{
private static final class CacheMiss
{
}
private static final CacheMiss CACHE_MISS = new CacheMiss();
private static final Object OBJECT = new Object();
/**
* Class passed into the constructor used to as
* the basis for the Method map.
*/
private Class clazz;
/**
* Cache of Methods, or CACHE_MISS, keyed by method
* name and actual arguments used to find it.
*/
private Map methodCache = new Hashtable();
private MethodMap methodMap = new MethodMap();
/**
* Standard constructor
*/
public ClassMap( Class clazz )
{
this.clazz = clazz;
populateMethodCache();
}
/**
* @return the class object whose methods are cached by this map.
*/
Class getCachedClass()
{
return clazz;
}
/**
* Find a Method using the methodKey
* provided.
*
* Look in the methodMap for an entry. If found,
* it'll either be a CACHE_MISS, in which case we
* simply give up, or it'll be a Method, in which
* case, we return it.
*
* If nothing is found, then we must actually go
* and introspect the method from the MethodMap.
*/
public Method findMethod( String name, Object[] params )
throws MethodMap.AmbiguousException
{
String methodKey = makeMethodKey( name, params );
Object cacheEntry = methodCache.get( methodKey );
if ( cacheEntry == CACHE_MISS )
{
return null;
}
if ( cacheEntry == null )
{
try
{
cacheEntry = methodMap.find( name,
params );
}
catch ( MethodMap.AmbiguousException ae )
{
/*
* that's a miss :)
*/
methodCache.put( methodKey,
CACHE_MISS );
throw ae;
}
if ( cacheEntry == null )
{
methodCache.put( methodKey,
CACHE_MISS );
}
else
{
methodCache.put( methodKey,
cacheEntry );
}
}
// Yes, this might just be null.
return (Method) cacheEntry;
}
/**
* Populate the Map of direct hits. These
* are taken from all the public methods
* that our class provides.
*/
private void populateMethodCache()
{
StringBuffer methodKey;
/*
* get all publicly accessible methods
*/
Method[] methods = getAccessibleMethods( clazz );
/*
* map and cache them
*/
for ( int i = 0; i < methods.length; i++ )
{
Method method = methods[i];
/*
* now get the 'public method', the method declared by a
* public interface or class. (because the actual implementing
* class may be a facade...
*/
Method publicMethod = getPublicMethod( method );
/*
* it is entirely possible that there is no public method for
* the methods of this class (i.e. in the facade, a method
* that isn't on any of the interfaces or superclass
* in which case, ignore it. Otherwise, map and cache
*/
if ( publicMethod != null )
{
methodMap.add( publicMethod );
methodCache.put( makeMethodKey( publicMethod ), publicMethod );
}
}
}
/**
* Make a methodKey for the given method using
* the concatenation of the name and the
* types of the method parameters.
*/
private String makeMethodKey( Method method )
{
Class[] parameterTypes = method.getParameterTypes();
StringBuffer methodKey = new StringBuffer( method.getName() );
for ( int j = 0; j < parameterTypes.length; j++ )
{
/*
* If the argument type is primitive then we want
* to convert our primitive type signature to the
* corresponding Object type so introspection for
* methods with primitive types will work correctly.
*/
if ( parameterTypes[j].isPrimitive() )
{
if ( parameterTypes[j].equals( Boolean.TYPE ) )
methodKey.append( "java.lang.Boolean" );
else if ( parameterTypes[j].equals( Byte.TYPE ) )
methodKey.append( "java.lang.Byte" );
else if ( parameterTypes[j].equals( Character.TYPE ) )
methodKey.append( "java.lang.Character" );
else if ( parameterTypes[j].equals( Double.TYPE ) )
methodKey.append( "java.lang.Double" );
else if ( parameterTypes[j].equals( Float.TYPE ) )
methodKey.append( "java.lang.Float" );
else if ( parameterTypes[j].equals( Integer.TYPE ) )
methodKey.append( "java.lang.Integer" );
else if ( parameterTypes[j].equals( Long.TYPE ) )
methodKey.append( "java.lang.Long" );
else if ( parameterTypes[j].equals( Short.TYPE ) )
methodKey.append( "java.lang.Short" );
}
else
{
methodKey.append( parameterTypes[j].getName() );
}
}
return methodKey.toString();
}
private static String makeMethodKey( String method, Object[] params )
{
StringBuffer methodKey = new StringBuffer().append( method );
for ( int j = 0; j < params.length; j++ )
{
Object arg = params[j];
if ( arg == null )
{
arg = OBJECT;
}
methodKey.append( arg.getClass().getName() );
}
return methodKey.toString();
}
/**
* Retrieves public methods for a class. In case the class is not
* public, retrieves methods with same signature as its public methods
* from public superclasses and interfaces (if they exist). Basically
* upcasts every method to the nearest acccessible method.
*/
private static Method[] getAccessibleMethods( Class clazz )
{
Method[] methods = clazz.getMethods();
/*
* Short circuit for the (hopefully) majority of cases where the
* clazz is public
*/
if ( Modifier.isPublic( clazz.getModifiers() ) )
{
return methods;
}
/*
* No luck - the class is not public, so we're going the longer way.
*/
MethodInfo[] methodInfos = new MethodInfo[methods.length];
for ( int i = methods.length; i-- > 0; )
{
methodInfos[i] = new MethodInfo( methods[i] );
}
int upcastCount = getAccessibleMethods( clazz, methodInfos, 0 );
/*
* Reallocate array in case some method had no accessible counterpart.
*/
if ( upcastCount < methods.length )
{
methods = new Method[upcastCount];
}
int j = 0;
for ( int i = 0; i < methodInfos.length; ++i )
{
MethodInfo methodInfo = methodInfos[i];
if ( methodInfo.upcast )
{
methods[j++] = methodInfo.method;
}
}
return methods;
}
/**
* Recursively finds a match for each method, starting with the class, and then
* searching the superclass and interfaces.
*
* @param clazz Class to check
* @param methodInfos array of methods we are searching to match
* @param upcastCount current number of methods we have matched
* @return count of matched methods
*/
private static int getAccessibleMethods( Class clazz, MethodInfo[] methodInfos, int upcastCount )
{
int l = methodInfos.length;
/*
* if this class is public, then check each of the currently
* 'non-upcasted' methods to see if we have a match
*/
if ( Modifier.isPublic( clazz.getModifiers() ) )
{
for ( int i = 0; i < l && upcastCount < l; ++i )
{
try
{
MethodInfo methodInfo = methodInfos[i];
if ( !methodInfo.upcast )
{
methodInfo.tryUpcasting( clazz );
upcastCount++;
}
}
catch ( NoSuchMethodException e )
{
/*
* Intentionally ignored - it means
* it wasn't found in the current class
*/
}
}
/*
* Short circuit if all methods were upcast
*/
if ( upcastCount == l )
{
return upcastCount;
}
}
/*
* Examine superclass
*/
Class superclazz = clazz.getSuperclass();
if ( superclazz != null )
{
upcastCount = getAccessibleMethods( superclazz, methodInfos, upcastCount );
/*
* Short circuit if all methods were upcast
*/
if ( upcastCount == l )
{
return upcastCount;
}
}
/*
* Examine interfaces. Note we do it even if superclazz == null.
* This is redundant as currently java.lang.Object does not implement
* any interfaces, however nothing guarantees it will not in future.
*/
Class[] interfaces = clazz.getInterfaces();
for ( int i = interfaces.length; i-- > 0; )
{
upcastCount = getAccessibleMethods( interfaces[i], methodInfos, upcastCount );
/*
* Short circuit if all methods were upcast
*/
if ( upcastCount == l )
{
return upcastCount;
}
}
return upcastCount;
}
/**
* For a given method, retrieves its publicly accessible counterpart.
* This method will look for a method with same name
* and signature declared in a public superclass or implemented interface of this
* method's declaring class. This counterpart method is publicly callable.
*
* @param method a method whose publicly callable counterpart is requested.
* @return the publicly callable counterpart method. Note that if the parameter
* method is itself declared by a public class, this method is an identity
* function.
*/
public static Method getPublicMethod( Method method )
{
Class clazz = method.getDeclaringClass();
/*
* Short circuit for (hopefully the majority of) cases where the declaring
* class is public.
*/
if ( ( clazz.getModifiers() & Modifier.PUBLIC ) != 0 )
{
return method;
}
return getPublicMethod( clazz, method.getName(), method.getParameterTypes() );
}
/**
* Looks up the method with specified name and signature in the first public
* superclass or implemented interface of the class.
*
* @param class the class whose method is sought
* @param name the name of the method
* @param paramTypes the classes of method parameters
*/
private static Method getPublicMethod( Class clazz, String name, Class[] paramTypes )
{
/*
* if this class is public, then try to get it
*/
if ( ( clazz.getModifiers() & Modifier.PUBLIC ) != 0 )
{
try
{
return clazz.getMethod( name, paramTypes );
}
catch ( NoSuchMethodException e )
{
/*
* If the class does not have the method, then neither its
* superclass nor any of its interfaces has it so quickly return
* null.
*/
return null;
}
}
/*
* try the superclass
*/
Class superclazz = clazz.getSuperclass();
if ( superclazz != null )
{
Method superclazzMethod = getPublicMethod( superclazz, name, paramTypes );
if ( superclazzMethod != null )
{
return superclazzMethod;
}
}
/*
* and interfaces
*/
Class[] interfaces = clazz.getInterfaces();
for ( int i = 0; i < interfaces.length; ++i )
{
Method interfaceMethod = getPublicMethod( interfaces[i], name, paramTypes );
if ( interfaceMethod != null )
{
return interfaceMethod;
}
}
return null;
}
/**
* Used for the iterative discovery process for public methods.
*/
private static final class MethodInfo
{
Method method;
String name;
Class[] parameterTypes;
boolean upcast;
MethodInfo( Method method )
{
this.method = null;
name = method.getName();
parameterTypes = method.getParameterTypes();
upcast = false;
}
void tryUpcasting( Class clazz )
throws NoSuchMethodException
{
method = clazz.getMethod( name, parameterTypes );
name = null;
parameterTypes = null;
upcast = true;
}
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/introspection/MethodMap.java 0000664 0000000 0000000 00000034760 11660305702 0031224 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.introspection;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
*
* @author Jason van Zyl
* @author Bob McWhirter
* @author Christoph Reck
* @author Geir Magnusson Jr.
* @author Attila Szegedi
* @version $Id: MethodMap.java 8010 2009-01-07 12:59:50Z vsiveton $
*/
public class MethodMap
{
private static final int MORE_SPECIFIC = 0;
private static final int LESS_SPECIFIC = 1;
private static final int INCOMPARABLE = 2;
/**
* Keep track of all methods with the same name.
*/
Map methodByNameMap = new Hashtable();
/**
* Add a method to a list of methods by name.
* For a particular class we are keeping track
* of all the methods with the same name.
*/
public void add(Method method)
{
String methodName = method.getName();
List l = get( methodName );
if ( l == null)
{
l = new ArrayList();
methodByNameMap.put(methodName, l);
}
l.add(method);
return;
}
/**
* Return a list of methods with the same name.
*
* @param key The name of the method.
* @return List list of methods
*/
public List get(String key)
{
return (List) methodByNameMap.get(key);
}
/**
*
* Find a method. Attempts to find the
* most specific applicable method using the
* algorithm described in the JLS section
* 15.12.2 (with the exception that it can't
* distinguish a primitive type argument from
* an object type argument, since in reflection
* primitive type arguments are represented by
* their object counterparts, so for an argument of
* type (say) java.lang.Integer, it will not be able
* to decide between a method that takes int and a
* method that takes java.lang.Integer as a parameter.
*
*
*
* This turns out to be a relatively rare case
* where this is needed - however, functionality
* like this is needed.
*
*
* @param methodName name of method
* @param args the actual arguments with which the method is called
* @return the most specific applicable method, or null if no
* method is applicable.
* @throws AmbiguousException if there is more than one maximally
* specific applicable method
*/
public Method find(String methodName, Object[] args)
throws AmbiguousException
{
List methodList = get(methodName);
if (methodList == null)
{
return null;
}
int l = args.length;
Class[] classes = new Class[l];
for(int i = 0; i < l; ++i)
{
Object arg = args[i];
/*
* if we are careful down below, a null argument goes in there
* so we can know that the null was passed to the method
*/
classes[i] =
arg == null ? null : arg.getClass();
}
return getMostSpecific(methodList, classes);
}
/**
* simple distinguishable exception, used when
* we run across ambiguous overloading
*/
public static class AmbiguousException extends Exception
{
}
private static Method getMostSpecific(List methods, Class[] classes)
throws AmbiguousException
{
LinkedList applicables = getApplicables(methods, classes);
if(applicables.isEmpty())
{
return null;
}
if(applicables.size() == 1)
{
return (Method)applicables.getFirst();
}
/*
* This list will contain the maximally specific methods. Hopefully at
* the end of the below loop, the list will contain exactly one method,
* (the most specific method) otherwise we have ambiguity.
*/
LinkedList maximals = new LinkedList();
for (Iterator applicable = applicables.iterator();
applicable.hasNext();)
{
Method app = (Method) applicable.next();
Class[] appArgs = app.getParameterTypes();
boolean lessSpecific = false;
for (Iterator maximal = maximals.iterator();
!lessSpecific && maximal.hasNext();)
{
Method max = (Method) maximal.next();
switch(moreSpecific(appArgs, max.getParameterTypes()))
{
case MORE_SPECIFIC:
{
/*
* This method is more specific than the previously
* known maximally specific, so remove the old maximum.
*/
maximal.remove();
break;
}
case LESS_SPECIFIC:
{
/*
* This method is less specific than some of the
* currently known maximally specific methods, so we
* won't add it into the set of maximally specific
* methods
*/
lessSpecific = true;
break;
}
}
}
if(!lessSpecific)
{
maximals.addLast(app);
}
}
if(maximals.size() > 1)
{
// We have more than one maximally specific method
throw new AmbiguousException();
}
return (Method)maximals.getFirst();
}
/**
* Determines which method signature (represented by a class array) is more
* specific. This defines a partial ordering on the method signatures.
* @param c1 first signature to compare
* @param c2 second signature to compare
* @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
* c1 is less specific than c2, INCOMPARABLE if they are incomparable.
*/
private static int moreSpecific(Class[] c1, Class[] c2)
{
boolean c1MoreSpecific = false;
boolean c2MoreSpecific = false;
for(int i = 0; i < c1.length; ++i)
{
if(c1[i] != c2[i])
{
c1MoreSpecific =
c1MoreSpecific ||
isStrictMethodInvocationConvertible(c2[i], c1[i]);
c2MoreSpecific =
c2MoreSpecific ||
isStrictMethodInvocationConvertible(c1[i], c2[i]);
}
}
if(c1MoreSpecific)
{
if(c2MoreSpecific)
{
/*
* Incomparable due to cross-assignable arguments (i.e.
* foo(String, Object) vs. foo(Object, String))
*/
return INCOMPARABLE;
}
return MORE_SPECIFIC;
}
if(c2MoreSpecific)
{
return LESS_SPECIFIC;
}
/*
* Incomparable due to non-related arguments (i.e.
* foo(Runnable) vs. foo(Serializable))
*/
return INCOMPARABLE;
}
/**
* Returns all methods that are applicable to actual argument types.
* @param methods list of all candidate methods
* @param classes the actual types of the arguments
* @return a list that contains only applicable methods (number of
* formal and actual arguments matches, and argument types are assignable
* to formal types through a method invocation conversion).
*/
private static LinkedList getApplicables(List methods, Class[] classes)
{
LinkedList list = new LinkedList();
for (Iterator imethod = methods.iterator(); imethod.hasNext();)
{
Method method = (Method) imethod.next();
if(isApplicable(method, classes))
{
list.add(method);
}
}
return list;
}
/**
* Returns true if the supplied method is applicable to actual
* argument types.
*/
private static boolean isApplicable(Method method, Class[] classes)
{
Class[] methodArgs = method.getParameterTypes();
if(methodArgs.length != classes.length)
{
return false;
}
for(int i = 0; i < classes.length; ++i)
{
if(!isMethodInvocationConvertible(methodArgs[i], classes[i]))
{
return false;
}
}
return true;
}
/**
* Determines whether a type represented by a class object is
* convertible to another type represented by a class object using a
* method invocation conversion, treating object types of primitive
* types as if they were primitive types (that is, a Boolean actual
* parameter type matches boolean primitive formal type). This behavior
* is because this method is used to determine applicable methods for
* an actual parameter list, and primitive types are represented by
* their object duals in reflective method calls.
*
* @param formal the formal parameter type to which the actual
* parameter type should be convertible
* @param actual the actual parameter type.
* @return true if either formal type is assignable from actual type,
* or formal is a primitive type and actual is its corresponding object
* type or an object type of a primitive type that can be converted to
* the formal type.
*/
private static boolean isMethodInvocationConvertible(Class formal,
Class actual)
{
/*
* if it's a null, it means the arg was null
*/
if (actual == null && !formal.isPrimitive())
{
return true;
}
/*
* Check for identity or widening reference conversion
*/
if (actual != null && formal.isAssignableFrom(actual))
{
return true;
}
/*
* Check for boxing with widening primitive conversion. Note that
* actual parameters are never primitives.
*/
if (formal.isPrimitive())
{
if(formal == Boolean.TYPE && actual == Boolean.class)
return true;
if(formal == Character.TYPE && actual == Character.class)
return true;
if(formal == Byte.TYPE && actual == Byte.class)
return true;
if(formal == Short.TYPE &&
(actual == Short.class || actual == Byte.class))
return true;
if(formal == Integer.TYPE &&
(actual == Integer.class || actual == Short.class ||
actual == Byte.class))
return true;
if(formal == Long.TYPE &&
(actual == Long.class || actual == Integer.class ||
actual == Short.class || actual == Byte.class))
return true;
if(formal == Float.TYPE &&
(actual == Float.class || actual == Long.class ||
actual == Integer.class || actual == Short.class ||
actual == Byte.class))
return true;
if(formal == Double.TYPE &&
(actual == Double.class || actual == Float.class ||
actual == Long.class || actual == Integer.class ||
actual == Short.class || actual == Byte.class))
return true;
}
return false;
}
/**
* Determines whether a type represented by a class object is
* convertible to another type represented by a class object using a
* method invocation conversion, without matching object and primitive
* types. This method is used to determine the more specific type when
* comparing signatures of methods.
*
* @param formal the formal parameter type to which the actual
* parameter type should be convertible
* @param actual the actual parameter type.
* @return true if either formal type is assignable from actual type,
* or formal and actual are both primitive types and actual can be
* subject to widening conversion to formal.
*/
private static boolean isStrictMethodInvocationConvertible(Class formal,
Class actual)
{
/*
* we shouldn't get a null into, but if so
*/
if (actual == null && !formal.isPrimitive())
{
return true;
}
/*
* Check for identity or widening reference conversion
*/
if(formal.isAssignableFrom(actual))
{
return true;
}
/*
* Check for widening primitive conversion.
*/
if(formal.isPrimitive())
{
if(formal == Short.TYPE && (actual == Byte.TYPE))
return true;
if(formal == Integer.TYPE &&
(actual == Short.TYPE || actual == Byte.TYPE))
return true;
if(formal == Long.TYPE &&
(actual == Integer.TYPE || actual == Short.TYPE ||
actual == Byte.TYPE))
return true;
if(formal == Float.TYPE &&
(actual == Long.TYPE || actual == Integer.TYPE ||
actual == Short.TYPE || actual == Byte.TYPE))
return true;
if(formal == Double.TYPE &&
(actual == Float.TYPE || actual == Long.TYPE ||
actual == Integer.TYPE || actual == Short.TYPE ||
actual == Byte.TYPE))
return true;
}
return false;
}
}
ReflectionValueExtractor.java 0000664 0000000 0000000 00000021673 11660305702 0034251 0 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/introspection package org.codehaus.plexus.util.introspection;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.WeakHashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.codehaus.plexus.util.StringUtils;
/**
* Using simple dotted expressions to extract the values from an Object instance,
* For example we might want to extract a value like: project.build.sourceDirectory
*
* The implementation supports indexed, nested and mapped properties similar to the JSP way.
*
* @author Jason van Zyl
* @author Vincent Siveton
* @version $Id: ReflectionValueExtractor.java 8010 2009-01-07 12:59:50Z vsiveton $
* @see http://struts.apache.org/1.x/struts-taglib/indexedprops.html
*/
public class ReflectionValueExtractor
{
private static final Class[] CLASS_ARGS = new Class[0];
private static final Object[] OBJECT_ARGS = new Object[0];
/**
* Use a WeakHashMap here, so the keys (Class objects) can be garbage collected.
* This approach prevents permgen space overflows due to retention of discarded
* classloaders.
*/
private static final Map classMaps = new WeakHashMap();
/**
* Indexed properties pattern, ie (\\w+)\\[(\\d+)\\]
*/
private static final Pattern INDEXED_PROPS = Pattern.compile( "(\\w+)\\[(\\d+)\\]" );
/**
* Indexed properties pattern, ie (\\w+)\\((.+)\\)
*/
private static final Pattern MAPPED_PROPS = Pattern.compile( "(\\w+)\\((.+)\\)" );
private ReflectionValueExtractor()
{
}
/**
* The implementation supports indexed, nested and mapped properties.
*
*
* - nested properties should be defined by a dot, i.e. "user.address.street"
* - indexed properties (java.util.List or array instance) should be contains
(\\w+)\\[(\\d+)\\]
* pattern, i.e. "user.addresses[1].street"
* - mapped properties should be contains
(\\w+)\\((.+)\\)
pattern, i.e. "user.addresses(myAddress).street"
*
*
* @param expression not null expression
* @param root not null object
* @return the object defined by the expression
* @throws Exception if any
*/
public static Object evaluate( String expression, Object root )
throws Exception
{
return evaluate( expression, root, true );
}
/**
* The implementation supports indexed, nested and mapped properties.
*
*
* - nested properties should be defined by a dot, i.e. "user.address.street"
* - indexed properties (java.util.List or array instance) should be contains
(\\w+)\\[(\\d+)\\]
* pattern, i.e. "user.addresses[1].street"
* - mapped properties should be contains
(\\w+)\\((.+)\\)
pattern, i.e. "user.addresses(myAddress).street"
*
*
* @param expression not null expression
* @param root not null object
* @return the object defined by the expression
* @throws Exception if any
*/
// TODO: don't throw Exception
public static Object evaluate( String expression, Object root, boolean trimRootToken )
throws Exception
{
// if the root token refers to the supplied root object parameter, remove it.
if ( trimRootToken )
{
expression = expression.substring( expression.indexOf( '.' ) + 1 );
}
Object value = root;
// ----------------------------------------------------------------------
// Walk the dots and retrieve the ultimate value desired from the
// MavenProject instance.
// ----------------------------------------------------------------------
StringTokenizer parser = new StringTokenizer( expression, "." );
while ( parser.hasMoreTokens() )
{
// if we have nothing, stop now
if ( value == null )
{
return null;
}
String token = parser.nextToken();
ClassMap classMap = getClassMap( value.getClass() );
Method method;
Object[] localParams = OBJECT_ARGS;
// do we have an indexed property?
Matcher matcher = INDEXED_PROPS.matcher( token );
if ( matcher.find() )
{
String methodBase = StringUtils.capitalizeFirstLetter( matcher.group( 1 ) );
String methodName = "get" + methodBase;
method = classMap.findMethod( methodName, CLASS_ARGS );
value = method.invoke( value, OBJECT_ARGS );
classMap = getClassMap( value.getClass() );
if ( classMap.getCachedClass().isArray() )
{
value = Arrays.asList( (Object[]) value );
classMap = getClassMap( value.getClass() );
}
if ( value instanceof List )
{
// use get method on List interface
localParams = new Object[1];
localParams[0] = Integer.valueOf( matcher.group( 2 ) );
method = classMap.findMethod( "get", localParams );
}
else
{
throw new Exception( "The token '" + token
+ "' refers to a java.util.List or an array, but the value seems is an instance of '"
+ value.getClass() + "'." );
}
}
else
{
// do we have a mapped property?
matcher = MAPPED_PROPS.matcher( token );
if ( matcher.find() )
{
String methodBase = StringUtils.capitalizeFirstLetter( matcher.group( 1 ) );
String methodName = "get" + methodBase;
method = classMap.findMethod( methodName, CLASS_ARGS );
value = method.invoke( value, OBJECT_ARGS );
classMap = getClassMap( value.getClass() );
if ( value instanceof Map )
{
// use get method on List interface
localParams = new Object[1];
localParams[0] = matcher.group( 2 );
method = classMap.findMethod( "get", localParams );
}
else
{
throw new Exception( "The token '" + token
+ "' refers to a java.util.Map, but the value seems is an instance of '"
+ value.getClass() + "'." );
}
}
else
{
String methodBase = StringUtils.capitalizeFirstLetter( token );
String methodName = "get" + methodBase;
method = classMap.findMethod( methodName, CLASS_ARGS );
if ( method == null )
{
// perhaps this is a boolean property??
methodName = "is" + methodBase;
method = classMap.findMethod( methodName, CLASS_ARGS );
}
}
}
if ( method == null )
{
return null;
}
try
{
value = method.invoke( value, localParams );
}
catch ( InvocationTargetException e )
{
// catch array index issues gracefully, otherwise release
if ( e.getCause() instanceof IndexOutOfBoundsException )
{
return null;
}
throw e;
}
}
return value;
}
private static ClassMap getClassMap( Class clazz )
{
ClassMap classMap = (ClassMap) classMaps.get( clazz );
if ( classMap == null )
{
classMap = new ClassMap( clazz );
classMaps.put( clazz, classMap );
}
return classMap;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/io/ 0000775 0000000 0000000 00000000000 11660305702 0024200 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/io/FileInputStreamFacade.java 0000664 0000000 0000000 00000002211 11660305702 0031176 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.io;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Implementation of {@link InputStreamFacade} for files.
*/
public class FileInputStreamFacade implements InputStreamFacade {
private final File file;
/**
* Creates a new instance.
*/
public FileInputStreamFacade( File file )
{
this.file = file;
}
public InputStream getInputStream() throws IOException {
return new FileInputStream( file );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/io/InputStreamFacade.java 0000664 0000000 0000000 00000002075 11660305702 0030406 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.io;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.IOException;
import java.io.InputStream;
/**
* Interface of a wrapper for input streams. This facade is used
* by methods, which are being implemented for files, URL's, or
* input streams.
*/
public interface InputStreamFacade {
/**
* Retrieves the actual {@link InputStream}. The caller must assume,
* that this method may be invoked only once.
*/
InputStream getInputStream() throws IOException;
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/io/RawInputStreamFacade.java 0000664 0000000 0000000 00000002125 11660305702 0031054 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.io;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.IOException;
import java.io.InputStream;
/**
* Implementation of {@link InputStreamFacade} for raw input streams.
*/
public class RawInputStreamFacade implements InputStreamFacade {
final InputStream stream;
/**
* Creates a new instance.
*/
public RawInputStreamFacade( InputStream stream )
{
this.stream = stream;
}
public InputStream getInputStream() throws IOException {
return stream;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/io/URLInputStreamFacade.java 0000664 0000000 0000000 00000002124 11660305702 0030764 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.io;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* Implementation of {@link InputStreamFacade} for URL's.
*/
public class URLInputStreamFacade implements InputStreamFacade {
private final URL url;
/**
* Creates a new instance.
*/
public URLInputStreamFacade( URL url )
{
this.url = url;
}
public InputStream getInputStream() throws IOException {
return url.openStream();
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/reflection/ 0000775 0000000 0000000 00000000000 11660305702 0025723 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/reflection/Reflector.java 0000664 0000000 0000000 00000047607 11660305702 0030531 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.reflection;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* Utility class used to instantiate an object using reflection. This utility
* hides many of the gory details needed to do this.
*
* @author John Casey
*/
public final class Reflector
{
private static final String CONSTRUCTOR_METHOD_NAME = "$$CONSTRUCTOR$$";
private static final String GET_INSTANCE_METHOD_NAME = "getInstance";
private HashMap classMaps = new HashMap();
/** Ensure no instances of Reflector are created...this is a utility. */
public Reflector()
{
}
/**
* Create a new instance of a class, given the array of parameters... Uses
* constructor caching to find a constructor that matches the parameter
* types, either specifically (first choice) or abstractly...
*
* @param theClass
* The class to instantiate
* @param params
* The parameters to pass to the constructor
* @return The instantiated object
* @throws ReflectorException
* In case anything goes wrong here...
*/
public Object newInstance( Class theClass, Object[] params )
throws ReflectorException
{
if ( params == null )
{
params = new Object[0];
}
Class[] paramTypes = new Class[params.length];
for ( int i = 0, len = params.length; i < len; i++ )
{
paramTypes[i] = params[i].getClass();
}
try
{
Constructor con = getConstructor( theClass, paramTypes );
if ( con == null )
{
StringBuffer buffer = new StringBuffer();
buffer.append( "Constructor not found for class: " );
buffer.append( theClass.getName() );
buffer.append( " with specified or ancestor parameter classes: " );
for ( int i = 0; i < paramTypes.length; i++ )
{
buffer.append( paramTypes[i].getName() );
buffer.append( ',' );
}
buffer.setLength( buffer.length() - 1 );
throw new ReflectorException( buffer.toString() );
}
return con.newInstance( params );
}
catch ( InstantiationException ex )
{
throw new ReflectorException( ex );
}
catch ( InvocationTargetException ex )
{
throw new ReflectorException( ex );
}
catch ( IllegalAccessException ex )
{
throw new ReflectorException( ex );
}
}
/**
* Retrieve the singleton instance of a class, given the array of
* parameters... Uses constructor caching to find a constructor that matches
* the parameter types, either specifically (first choice) or abstractly...
*
* @param theClass
* The class to retrieve the singleton of
* @param initParams
* The parameters to pass to the constructor
* @return The singleton object
* @throws ReflectorException
* In case anything goes wrong here...
*/
public Object getSingleton( Class theClass, Object[] initParams )
throws ReflectorException
{
Class[] paramTypes = new Class[initParams.length];
for ( int i = 0, len = initParams.length; i < len; i++ )
{
paramTypes[i] = initParams[i].getClass();
}
try
{
Method method = getMethod( theClass, GET_INSTANCE_METHOD_NAME, paramTypes );
return method.invoke( null, initParams );
}
catch ( InvocationTargetException ex )
{
throw new ReflectorException( ex );
}
catch ( IllegalAccessException ex )
{
throw new ReflectorException( ex );
}
}
/**
* Invoke the specified method on the specified target with the specified
* params...
*
* @param target
* The target of the invocation
* @param methodName
* The method name to invoke
* @param params
* The parameters to pass to the method invocation
* @return The result of the method call
* @throws ReflectorException
* In case of an error looking up or invoking the method.
*/
public Object invoke( Object target, String methodName, Object[] params )
throws ReflectorException
{
if ( params == null )
{
params = new Object[0];
}
Class[] paramTypes = new Class[params.length];
for ( int i = 0, len = params.length; i < len; i++ )
{
paramTypes[i] = params[i].getClass();
}
try
{
Method method = getMethod( target.getClass(), methodName, paramTypes );
if ( method == null )
{
StringBuffer buffer = new StringBuffer();
buffer.append( "Singleton-producing method named '" ).append( methodName )
.append( "' not found with specified parameter classes: " );
for ( int i = 0; i < paramTypes.length; i++ )
{
buffer.append( paramTypes[i].getName() );
buffer.append( ',' );
}
buffer.setLength( buffer.length() - 1 );
throw new ReflectorException( buffer.toString() );
}
return method.invoke( target, params );
}
catch ( InvocationTargetException ex )
{
throw new ReflectorException( ex );
}
catch ( IllegalAccessException ex )
{
throw new ReflectorException( ex );
}
}
public Object getStaticField( Class targetClass, String fieldName )
throws ReflectorException
{
try
{
Field field = targetClass.getField( fieldName );
return field.get( null );
}
catch ( SecurityException e )
{
throw new ReflectorException( e );
}
catch ( NoSuchFieldException e )
{
throw new ReflectorException( e );
}
catch ( IllegalArgumentException e )
{
throw new ReflectorException( e );
}
catch ( IllegalAccessException e )
{
throw new ReflectorException( e );
}
}
public Object getField( Object target, String fieldName )
throws ReflectorException
{
return getField( target, fieldName, false );
}
public Object getField( Object target, String fieldName, boolean breakAccessibility )
throws ReflectorException
{
Class targetClass = target.getClass();
while ( targetClass != null )
{
try
{
Field field = targetClass.getDeclaredField( fieldName );
boolean accessibilityBroken = false;
if ( !field.isAccessible() && breakAccessibility )
{
field.setAccessible( true );
accessibilityBroken = true;
}
Object result = field.get( target );
if ( accessibilityBroken )
{
field.setAccessible( false );
}
return result;
}
catch ( SecurityException e )
{
throw new ReflectorException( e );
}
catch ( NoSuchFieldException e )
{
if ( targetClass == Object.class )
throw new ReflectorException( e );
targetClass = targetClass.getSuperclass();
}
catch ( IllegalAccessException e )
{
throw new ReflectorException( e );
}
}
// Never reached, but needed to satisfy compiler
return null;
}
/**
* Invoke the specified static method with the specified params...
*
* @param targetClass
* The target class of the invocation
* @param methodName
* The method name to invoke
* @param params
* The parameters to pass to the method invocation
* @return The result of the method call
* @throws ReflectorException
* In case of an error looking up or invoking the method.
*/
public Object invokeStatic( Class targetClass, String methodName, Object[] params )
throws ReflectorException
{
if ( params == null )
{
params = new Object[0];
}
Class[] paramTypes = new Class[params.length];
for ( int i = 0, len = params.length; i < len; i++ )
{
paramTypes[i] = params[i].getClass();
}
try
{
Method method = getMethod( targetClass, methodName, paramTypes );
if ( method == null )
{
StringBuffer buffer = new StringBuffer();
buffer.append( "Singleton-producing method named \'" + methodName
+ "\' not found with specified parameter classes: " );
for ( int i = 0; i < paramTypes.length; i++ )
{
buffer.append( paramTypes[i].getName() );
buffer.append( ',' );
}
buffer.setLength( buffer.length() - 1 );
throw new ReflectorException( buffer.toString() );
}
return method.invoke( null, params );
}
catch ( InvocationTargetException ex )
{
throw new ReflectorException( ex );
}
catch ( IllegalAccessException ex )
{
throw new ReflectorException( ex );
}
}
/**
* Return the constructor, checking the cache first and storing in cache if
* not already there..
*
* @param targetClass
* The class to get the constructor from
* @param params
* The classes of the parameters which the constructor should
* match.
* @return the Constructor object that matches.
* @throws ReflectorException
* In case we can't retrieve the proper constructor.
*/
public Constructor getConstructor( Class targetClass, Class[] params )
throws ReflectorException
{
Map constructorMap = getConstructorMap( targetClass );
StringBuffer key = new StringBuffer( 200 );
key.append( "(" );
for ( int i = 0, len = params.length; i < len; i++ )
{
key.append( params[i].getName() );
key.append( "," );
}
if ( params.length > 0 )
{
key.setLength( key.length() - 1 );
}
key.append( ")" );
Constructor constructor = null;
String paramKey = key.toString();
synchronized ( paramKey.intern() )
{
constructor = (Constructor) constructorMap.get( paramKey );
if ( constructor == null )
{
Constructor[] cands = targetClass.getConstructors();
for ( int i = 0, len = cands.length; i < len; i++ )
{
Class[] types = cands[i].getParameterTypes();
if ( params.length != types.length )
{
continue;
}
for ( int j = 0, len2 = params.length; j < len2; j++ )
{
if ( !types[j].isAssignableFrom( params[j] ) )
{
continue;
}
}
// we got it, so store it!
constructor = cands[i];
constructorMap.put( paramKey, constructor );
}
}
}
if ( constructor == null )
{
throw new ReflectorException( "Error retrieving constructor object for: " + targetClass.getName()
+ paramKey );
}
return constructor;
}
public Object getObjectProperty( Object target, String propertyName )
throws ReflectorException
{
Object returnValue = null;
if ( propertyName == null || propertyName.trim().length() < 1 )
{
throw new ReflectorException( "Cannot retrieve value for empty property." );
}
String beanAccessor = "get" + Character.toUpperCase( propertyName.charAt( 0 ) );
if ( propertyName.trim().length() > 1 )
{
beanAccessor += propertyName.substring( 1 ).trim();
}
Class targetClass = target.getClass();
Class[] emptyParams = {};
Method method = _getMethod( targetClass, beanAccessor, emptyParams );
if ( method == null )
{
method = _getMethod( targetClass, propertyName, emptyParams );
}
if ( method != null )
{
try
{
returnValue = method.invoke( target, new Object[] {} );
}
catch ( IllegalAccessException e )
{
throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'"
+ targetClass + "\'", e );
}
catch ( InvocationTargetException e )
{
throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'"
+ targetClass + "\'", e );
}
}
if ( method != null )
{
try
{
returnValue = method.invoke( target, new Object[] {} );
}
catch ( IllegalAccessException e )
{
throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'"
+ targetClass + "\'", e );
}
catch ( InvocationTargetException e )
{
throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'"
+ targetClass + "\'", e );
}
}
else
{
returnValue = getField( target, propertyName, true );
if ( method == null && returnValue == null )
{
// TODO: Check if exception is the right action! Field exists, but contains null
throw new ReflectorException( "Neither method: \'" + propertyName + "\' nor bean accessor: \'"
+ beanAccessor + "\' can be found for class: \'" + targetClass
+ "\', and retrieval of field: \'" + propertyName + "\' returned null as value." );
}
}
return returnValue;
}
/**
* Return the method, checking the cache first and storing in cache if not
* already there..
*
* @param targetClass
* The class to get the method from
* @param params
* The classes of the parameters which the method should match.
* @return the Method object that matches.
* @throws ReflectorException
* In case we can't retrieve the proper method.
*/
public Method getMethod( Class targetClass, String methodName, Class[] params )
throws ReflectorException
{
Method method = _getMethod( targetClass, methodName, params );
if ( method == null )
{
throw new ReflectorException( "Method: \'" + methodName + "\' not found in class: \'" + targetClass
+ "\'" );
}
return method;
}
private Method _getMethod( Class targetClass, String methodName, Class[] params )
throws ReflectorException
{
Map methodMap = getMethodMap( targetClass, methodName );
StringBuffer key = new StringBuffer( 200 );
key.append( "(" );
for ( int i = 0, len = params.length; i < len; i++ )
{
key.append( params[i].getName() );
key.append( "," );
}
key.append( ")" );
Method method = null;
String paramKey = key.toString();
synchronized ( paramKey.intern() )
{
method = (Method) methodMap.get( paramKey );
if ( method == null )
{
Method[] cands = targetClass.getMethods();
for ( int i = 0, len = cands.length; i < len; i++ )
{
String name = cands[i].getName();
if ( !methodName.equals( name ) )
{
continue;
}
Class[] types = cands[i].getParameterTypes();
if ( params.length != types.length )
{
continue;
}
for ( int j = 0, len2 = params.length; j < len2; j++ )
{
if ( !types[j].isAssignableFrom( params[j] ) )
{
continue;
}
}
// we got it, so store it!
method = cands[i];
methodMap.put( paramKey, method );
}
}
}
return method;
}
/**
* Retrieve the cache of constructors for the specified class.
*
* @param theClass
* the class to lookup.
* @return The cache of constructors.
* @throws ReflectorException
* in case of a lookup error.
*/
private Map getConstructorMap( Class theClass )
throws ReflectorException
{
return getMethodMap( theClass, CONSTRUCTOR_METHOD_NAME );
}
/**
* Retrieve the cache of methods for the specified class and method name.
*
* @param theClass
* the class to lookup.
* @param methodName
* The name of the method to lookup.
* @return The cache of constructors.
* @throws ReflectorException
* in case of a lookup error.
*/
private Map getMethodMap( Class theClass, String methodName )
throws ReflectorException
{
Map methodMap = null;
if ( theClass == null )
{
return null;
}
String className = theClass.getName();
synchronized ( className.intern() )
{
Map classMethods = (Map) classMaps.get( className );
if ( classMethods == null )
{
classMethods = new HashMap();
methodMap = new HashMap();
classMethods.put( methodName, methodMap );
classMaps.put( className, classMethods );
}
else
{
String key = className + "::" + methodName;
synchronized ( key.intern() )
{
methodMap = (Map) classMethods.get( methodName );
if ( methodMap == null )
{
methodMap = new HashMap();
classMethods.put( methodName, methodMap );
}
}
}
}
return methodMap;
}
} plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/reflection/ReflectorException.java 0000664 0000000 0000000 00000003464 11660305702 0032401 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.reflection;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
/**
* Exception indicating that an error has occurred while instantiating a class
* with the Reflector class. This exception is meant to put a more user-friendly
* face on the myriad other exceptions throws during reflective object creation.
*
* @author John Casey
*/
public class ReflectorException
extends Exception
{
/**
* Create a new ReflectorException.
*/
public ReflectorException()
{
}
/**
* Create a new ReflectorException with the specified message.
*
* @param msg
* The message.
*/
public ReflectorException( String msg )
{
super( msg );
}
/**
* Create a new ReflectorException with the specified root cause.
*
* @param root
* The root cause.
*/
public ReflectorException( Throwable root )
{
super( root );
}
/**
* Create a new ReflectorException with the specified message and root
* cause.
*
* @param msg
* The message.
* @param root
* The root cause.
*/
public ReflectorException( String msg, Throwable root )
{
super( msg, root );
}
} plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/ 0000775 0000000 0000000 00000000000 11660305702 0024371 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/CompactXMLWriter.java 0000664 0000000 0000000 00000002145 11660305702 0030402 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.xml;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.PrintWriter;
import java.io.Writer;
/**
* @version $Id: CompactXMLWriter.java 8660 2010-03-05 21:50:36Z hboutemy $
*/
public class CompactXMLWriter
extends PrettyPrintXMLWriter
{
public CompactXMLWriter( PrintWriter writer )
{
super( writer );
}
public CompactXMLWriter( Writer writer )
{
super( writer );
}
protected void endOfLine()
{
// override parent: don't write anything at end of line
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/PrettyPrintXMLWriter.java 0000664 0000000 0000000 00000025134 11660305702 0031323 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.xml;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.PrintWriter;
import java.io.Writer;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.codehaus.plexus.util.StringUtils;
/**
* Implementation of XMLWriter which emits nicely formatted documents.
*
* @version $Id: PrettyPrintXMLWriter.java 8660 2010-03-05 21:50:36Z hboutemy $
*/
public class PrettyPrintXMLWriter
implements XMLWriter
{
/** Line separator ("\n" on UNIX) */
protected static final String LS = System.getProperty( "line.separator" );
private PrintWriter writer;
private LinkedList elementStack = new LinkedList();
private boolean tagInProgress;
private int depth;
private String lineIndenter;
private String lineSeparator;
private String encoding;
private String docType;
private boolean readyForNewLine;
private boolean tagIsEmpty;
/**
* @param writer not null
* @param lineIndenter could be null, but the normal way is some spaces.
*/
public PrettyPrintXMLWriter( PrintWriter writer, String lineIndenter )
{
this( writer, lineIndenter, null, null );
}
/**
* @param writer not null
* @param lineIndenter could be null, but the normal way is some spaces.
*/
public PrettyPrintXMLWriter( Writer writer, String lineIndenter )
{
this( new PrintWriter( writer ), lineIndenter );
}
/**
* @param writer not null
*/
public PrettyPrintXMLWriter( PrintWriter writer )
{
this( writer, null, null );
}
/**
* @param writer not null
*/
public PrettyPrintXMLWriter( Writer writer )
{
this( new PrintWriter( writer ) );
}
/**
* @param writer not null
* @param lineIndenter could be null, but the normal way is some spaces.
* @param encoding could be null or invalid.
* @param doctype could be null.
*/
public PrettyPrintXMLWriter( PrintWriter writer, String lineIndenter, String encoding, String doctype )
{
this( writer, lineIndenter, LS, encoding, doctype );
}
/**
* @param writer not null
* @param lineIndenter could be null, but the normal way is some spaces.
* @param encoding could be null or invalid.
* @param doctype could be null.
*/
public PrettyPrintXMLWriter( Writer writer, String lineIndenter, String encoding, String doctype )
{
this( new PrintWriter( writer ), lineIndenter, encoding, doctype );
}
/**
* @param writer not null
* @param encoding could be null or invalid.
* @param doctype could be null.
*/
public PrettyPrintXMLWriter( PrintWriter writer, String encoding, String doctype )
{
this( writer, " ", encoding, doctype );
}
/**
* @param writer not null
* @param encoding could be null or invalid.
* @param doctype could be null.
*/
public PrettyPrintXMLWriter( Writer writer, String encoding, String doctype )
{
this( new PrintWriter( writer ), encoding, doctype );
}
/**
* @param writer not null
* @param lineIndenter could be null, but the normal way is some spaces.
* @param lineSeparator could be null, but the normal way is valid line separator ("\n" on UNIX).
* @param encoding could be null or invalid.
* @param doctype could be null.
*/
public PrettyPrintXMLWriter( PrintWriter writer, String lineIndenter, String lineSeparator, String encoding, String doctype )
{
setWriter( writer );
setLineIndenter( lineIndenter );
setLineSeparator( lineSeparator );
setEncoding( encoding );
setDocType( doctype );
if ( doctype != null || encoding != null )
{
writeDocumentHeaders();
}
}
/** {@inheritDoc} */
public void startElement( String name )
{
tagIsEmpty = false;
finishTag();
write( "<" );
write( name );
elementStack.addLast( name );
tagInProgress = true;
setDepth( getDepth() + 1 );
readyForNewLine = true;
tagIsEmpty = true;
}
/** {@inheritDoc} */
public void writeText( String text )
{
writeText( text, true );
}
/** {@inheritDoc} */
public void writeMarkup( String text )
{
writeText( text, false );
}
private void writeText( String text, boolean escapeXml )
{
readyForNewLine = false;
tagIsEmpty = false;
finishTag();
if ( escapeXml )
{
text = escapeXml( text );
}
write( StringUtils.unifyLineSeparators( text, lineSeparator ) );
}
private static String escapeXml( String text )
{
text = text.replaceAll( "&", "&" );
text = text.replaceAll( "<", "<" );
text = text.replaceAll( ">", ">" );
text = text.replaceAll( "\"", """ );
text = text.replaceAll( "\'", "'" );
return text;
}
private static String escapeXmlAttribute( String text )
{
text = escapeXml( text );
// Windows
text = text.replaceAll( "\r\n", "
" );
Pattern pattern = Pattern.compile( "([\000-\037])" );
Matcher m = pattern.matcher( text );
StringBuffer b = new StringBuffer();
while ( m.find() )
{
m = m.appendReplacement( b, "" + Integer.toString( m.group( 1 ).charAt( 0 ) ) + ";" );
}
m.appendTail( b );
return b.toString();
}
/** {@inheritDoc} */
public void addAttribute( String key, String value )
{
write( " " );
write( key );
write( "=\"" );
write( escapeXmlAttribute( value ) );
write( "\"" );
}
/** {@inheritDoc} */
public void endElement()
{
setDepth( getDepth() - 1 );
if ( tagIsEmpty )
{
write( "/" );
readyForNewLine = false;
finishTag();
elementStack.removeLast();
}
else
{
finishTag();
write( "" + elementStack.removeLast() + ">" );
}
readyForNewLine = true;
}
/**
* Write a string to the underlying writer
* @param str
*/
private void write( String str )
{
getWriter().write( str );
}
private void finishTag()
{
if ( tagInProgress )
{
write( ">" );
}
tagInProgress = false;
if ( readyForNewLine )
{
endOfLine();
}
readyForNewLine = false;
tagIsEmpty = false;
}
/**
* Get the string used as line indenter
*
* @return the line indenter
*/
protected String getLineIndenter()
{
return lineIndenter;
}
/**
* Set the string used as line indenter
*
* @param lineIndenter new line indenter, could be null, but the normal way is some spaces.
*/
protected void setLineIndenter( String lineIndenter )
{
this.lineIndenter = lineIndenter;
}
/**
* Get the string used as line separator or LS if not set.
*
* @return the line separator
* @see #LS
*/
protected String getLineSeparator()
{
return lineSeparator;
}
/**
* Set the string used as line separator
*
* @param lineSeparator new line separator, could be null but the normal way is valid line separator
* ("\n" on UNIX).
*/
protected void setLineSeparator( String lineSeparator )
{
this.lineSeparator = lineSeparator;
}
/**
* Write the end of line character (using specified line separator)
* and start new line with indentation
*
* @see #getLineIndenter()
* @see #getLineSeparator()
*/
protected void endOfLine()
{
write( getLineSeparator() );
for ( int i = 0; i < getDepth(); i++ )
{
write( getLineIndenter() );
}
}
private void writeDocumentHeaders()
{
write( "" );
endOfLine();
if ( getDocType() != null )
{
write( "" );
endOfLine();
}
}
/**
* Set the underlying writer
*
* @param writer not null writer
*/
protected void setWriter( PrintWriter writer )
{
if ( writer == null )
{
throw new IllegalArgumentException( "writer could not be null");
}
this.writer = writer;
}
/**
* Get the underlying writer
*
* @return the underlying writer
*/
protected PrintWriter getWriter()
{
return writer;
}
/**
* Set the depth in the xml indentation
*
* @param depth new depth
*/
protected void setDepth( int depth )
{
this.depth = depth;
}
/**
* Get the current depth in the xml indentation
*
* @return the current depth
*/
protected int getDepth()
{
return depth;
}
/**
* Set the encoding in the xml
*
* @param encoding new encoding
*/
protected void setEncoding( String encoding )
{
this.encoding = encoding;
}
/**
* Get the current encoding in the xml
*
* @return the current encoding
*/
protected String getEncoding()
{
return encoding;
}
/**
* Set the docType in the xml
*
* @param docType new docType
*/
protected void setDocType( String docType )
{
this.docType = docType;
}
/**
* Get the docType in the xml
*
* @return the current docType
*/
protected String getDocType()
{
return docType;
}
/**
* @return the current elementStack;
*/
protected LinkedList getElementStack()
{
return elementStack;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/SerializerXMLWriter.java 0000664 0000000 0000000 00000005604 11660305702 0031130 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.xml;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import org.codehaus.plexus.util.xml.pull.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
/**
* Write to an MXSerializer.
*
* @author Brett Porter
* @version $Id: SerializerXMLWriter.java 8010 2009-01-07 12:59:50Z vsiveton $
*/
public class SerializerXMLWriter
implements XMLWriter
{
private final XmlSerializer serializer;
private final String namespace;
private final Stack elements = new Stack();
private List exceptions;
public SerializerXMLWriter( String namespace, XmlSerializer serializer )
{
this.serializer = serializer;
this.namespace = namespace;
}
public void startElement( String name )
{
try
{
serializer.startTag( namespace, name );
elements.push( name );
}
catch ( IOException e )
{
storeException( e );
}
}
public void addAttribute( String key, String value )
{
try
{
serializer.attribute( namespace, key, value );
}
catch ( IOException e )
{
storeException( e );
}
}
public void writeText( String text )
{
try
{
serializer.text( text );
}
catch ( IOException e )
{
storeException( e );
}
}
public void writeMarkup( String text )
{
try
{
serializer.cdsect( text );
}
catch ( IOException e )
{
storeException( e );
}
}
public void endElement()
{
try
{
serializer.endTag( namespace, (String) elements.pop() );
}
catch ( IOException e )
{
storeException( e );
}
}
/**
* @todo Maybe the interface should allow IOExceptions on each?
*/
private void storeException( IOException e )
{
if ( exceptions == null )
{
exceptions = new ArrayList();
}
exceptions.add( e );
}
public List getExceptions()
{
return exceptions == null ? Collections.EMPTY_LIST : exceptions;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/XMLWriter.java 0000664 0000000 0000000 00000001654 11660305702 0027077 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.xml;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
/**
* @version $Id: XMLWriter.java 8660 2010-03-05 21:50:36Z hboutemy $
*/
public interface XMLWriter
{
void startElement( String name );
void addAttribute( String key, String value );
void writeText( String text );
void writeMarkup( String text );
void endElement();
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/XmlReader.java 0000664 0000000 0000000 00000070621 11660305702 0027125 0 ustar 00root root 0000000 0000000 /*
* Copyright 2004 Sun Microsystems, 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 org.codehaus.plexus.util.xml;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import java.net.URLConnection;
import java.net.HttpURLConnection;
import java.util.Locale;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.text.MessageFormat;
/**
* Character stream that handles (or at least attemtps to) all the necessary Voodo to figure out the charset encoding of
* the XML document within the stream.
*
* IMPORTANT: This class is not related in any way to the org.xml.sax.XMLReader. This one IS a character stream.
*
* All this has to be done without consuming characters from the stream, if not the XML parser will not recognized the
* document as a valid XML. This is not 100% true, but it's close enough (UTF-8 BOM is not handled by all parsers right
* now, XmlReader handles it and things work in all parsers).
*
* The XmlReader class handles the charset encoding of XML documents in Files, raw streams and HTTP streams by offering
* a wide set of constructors.
*
* By default the charset encoding detection is lenient, the constructor with the lenient flag can be used for an script
* (following HTTP MIME and XML specifications). All this is nicely explained by Mark Pilgrim in his blog, Determining the character encoding of a feed.
*
*
* @author Alejandro Abdelnur
* @version revision 1.17 taken on 26/06/2007 from Rome (see https://rome.dev.java.net/source/browse/rome/src/java/com/sun/syndication/io/XmlReader.java)
* @deprecated use XmlStreamReader
* @since 1.4.3
*/
public class XmlReader extends Reader
{
private static final int BUFFER_SIZE = 4096;
private static final String UTF_8 = "UTF-8";
private static final String US_ASCII = "US-ASCII";
private static final String UTF_16BE = "UTF-16BE";
private static final String UTF_16LE = "UTF-16LE";
private static final String UTF_16 = "UTF-16";
private static final String EBCDIC = "CP1047";
private static String _staticDefaultEncoding = null;
private Reader _reader;
private String _encoding;
private String _defaultEncoding;
/**
* Sets the default encoding to use if none is set in HTTP content-type, XML prolog and the rules based on
* content-type are not adequate.
If it is set to NULL the content-type based rules are used. By default
* it is NULL.
*
* @param encoding
* charset encoding to default to.
*/
public static void setDefaultEncoding( String encoding )
{
_staticDefaultEncoding = encoding;
}
/**
* Returns the default encoding to use if none is set in HTTP content-type, XML prolog and the rules based on
* content-type are not adequate. If it is NULL the content-type based rules are used.
*
* @return the default encoding to use.
*/
public static String getDefaultEncoding()
{
return _staticDefaultEncoding;
}
/**
* Creates a Reader for a File.
*
* It looks for the UTF-8 BOM first, if none sniffs the XML prolog charset, if this is also missing defaults to
* UTF-8.
*
* It does a lenient charset encoding detection, check the constructor with the lenient parameter for details.
*
*
* @param file
* File to create a Reader from.
* @throws IOException
* thrown if there is a problem reading the file.
*
*/
public XmlReader( File file ) throws IOException
{
this( new FileInputStream( file ) );
}
/**
* Creates a Reader for a raw InputStream.
*
* It follows the same logic used for files.
*
* It does a lenient charset encoding detection, check the constructor with the lenient parameter for details.
*
*
* @param is
* InputStream to create a Reader from.
* @throws IOException
* thrown if there is a problem reading the stream.
*
*/
public XmlReader( InputStream is ) throws IOException
{
this( is, true );
}
/**
* Creates a Reader for a raw InputStream.
*
* It follows the same logic used for files.
*
* If lenient detection is indicated and the detection above fails as per specifications it then attempts the
* following:
*
* If the content type was 'text/html' it replaces it with 'text/xml' and tries the detection again.
*
* Else if the XML prolog had a charset encoding that encoding is used.
*
* Else if the content type had a charset encoding that encoding is used.
*
* Else 'UTF-8' is used.
*
* If lenient detection is indicated an XmlStreamReaderException is never thrown.
*
*
* @param is
* InputStream to create a Reader from.
* @param lenient
* indicates if the charset encoding detection should be relaxed.
* @throws IOException
* thrown if there is a problem reading the stream.
* @throws XmlStreamReaderException
* thrown if the charset encoding could not be determined according to the specs.
*
*/
public XmlReader( InputStream is, boolean lenient ) throws IOException, XmlStreamReaderException
{
_defaultEncoding = _staticDefaultEncoding;
try
{
doRawStream( is, lenient );
}
catch ( XmlStreamReaderException ex )
{
if ( !lenient )
{
throw ex;
}
else
{
doLenientDetection( null, ex );
}
}
}
/**
* Creates a Reader using the InputStream of a URL.
*
* If the URL is not of type HTTP and there is not 'content-type' header in the fetched data it uses the same logic
* used for Files.
*
* If the URL is a HTTP Url or there is a 'content-type' header in the fetched data it uses the same logic used for
* an InputStream with content-type.
*
* It does a lenient charset encoding detection, check the constructor with the lenient parameter for details.
*
*
* @param url
* URL to create a Reader from.
* @throws IOException
* thrown if there is a problem reading the stream of the URL.
*
*/
public XmlReader( URL url ) throws IOException
{
this( url.openConnection() );
}
/**
* Creates a Reader using the InputStream of a URLConnection.
*
* If the URLConnection is not of type HttpURLConnection and there is not 'content-type' header in the fetched data
* it uses the same logic used for files.
*
* If the URLConnection is a HTTP Url or there is a 'content-type' header in the fetched data it uses the same logic
* used for an InputStream with content-type.
*
* It does a lenient charset encoding detection, check the constructor with the lenient parameter for details.
*
*
* @param conn
* URLConnection to create a Reader from.
* @throws IOException
* thrown if there is a problem reading the stream of the URLConnection.
*
*/
public XmlReader( URLConnection conn ) throws IOException
{
_defaultEncoding = _staticDefaultEncoding;
boolean lenient = true;
if ( conn instanceof HttpURLConnection )
{
try
{
doHttpStream( conn.getInputStream(), conn.getContentType(), lenient );
}
catch ( XmlStreamReaderException ex )
{
doLenientDetection( conn.getContentType(), ex );
}
}
else if ( conn.getContentType() != null )
{
try
{
doHttpStream( conn.getInputStream(), conn.getContentType(), lenient );
}
catch ( XmlStreamReaderException ex )
{
doLenientDetection( conn.getContentType(), ex );
}
}
else
{
try
{
doRawStream( conn.getInputStream(), lenient );
}
catch ( XmlStreamReaderException ex )
{
doLenientDetection( null, ex );
}
}
}
/**
* Creates a Reader using an InputStream an the associated content-type header.
*
* First it checks if the stream has BOM. If there is not BOM checks the content-type encoding. If there is not
* content-type encoding checks the XML prolog encoding. If there is not XML prolog encoding uses the default
* encoding mandated by the content-type MIME type.
*
* It does a lenient charset encoding detection, check the constructor with the lenient parameter for details.
*
*
* @param is
* InputStream to create the reader from.
* @param httpContentType
* content-type header to use for the resolution of the charset encoding.
* @throws IOException
* thrown if there is a problem reading the file.
*
*/
public XmlReader( InputStream is, String httpContentType ) throws IOException
{
this( is, httpContentType, true );
}
/**
* Creates a Reader using an InputStream an the associated content-type header. This constructor is lenient
* regarding the encoding detection.
*
* First it checks if the stream has BOM. If there is not BOM checks the content-type encoding. If there is not
* content-type encoding checks the XML prolog encoding. If there is not XML prolog encoding uses the default
* encoding mandated by the content-type MIME type.
*
* If lenient detection is indicated and the detection above fails as per specifications it then attempts the
* following:
*
* If the content type was 'text/html' it replaces it with 'text/xml' and tries the detection again.
*
* Else if the XML prolog had a charset encoding that encoding is used.
*
* Else if the content type had a charset encoding that encoding is used.
*
* Else 'UTF-8' is used.
*
* If lenient detection is indicated an XmlStreamReaderException is never thrown.
*
*
* @param is
* InputStream to create the reader from.
* @param httpContentType
* content-type header to use for the resolution of the charset encoding.
* @param lenient
* indicates if the charset encoding detection should be relaxed.
* @throws IOException
* thrown if there is a problem reading the file.
* @throws XmlStreamReaderException
* thrown if the charset encoding could not be determined according to the specs.
*
*/
public XmlReader( InputStream is, String httpContentType, boolean lenient, String defaultEncoding )
throws IOException, XmlStreamReaderException
{
_defaultEncoding = ( defaultEncoding == null ) ? _staticDefaultEncoding : defaultEncoding;
try
{
doHttpStream( is, httpContentType, lenient );
}
catch ( XmlStreamReaderException ex )
{
if ( !lenient )
{
throw ex;
}
else
{
doLenientDetection( httpContentType, ex );
}
}
}
/**
* Creates a Reader using an InputStream an the associated content-type header. This constructor is lenient
* regarding the encoding detection.
*
* First it checks if the stream has BOM. If there is not BOM checks the content-type encoding. If there is not
* content-type encoding checks the XML prolog encoding. If there is not XML prolog encoding uses the default
* encoding mandated by the content-type MIME type.
*
* If lenient detection is indicated and the detection above fails as per specifications it then attempts the
* following:
*
* If the content type was 'text/html' it replaces it with 'text/xml' and tries the detection again.
*
* Else if the XML prolog had a charset encoding that encoding is used.
*
* Else if the content type had a charset encoding that encoding is used.
*
* Else 'UTF-8' is used.
*
* If lenient detection is indicated an XmlStreamReaderException is never thrown.
*
*
* @param is
* InputStream to create the reader from.
* @param httpContentType
* content-type header to use for the resolution of the charset encoding.
* @param lenient
* indicates if the charset encoding detection should be relaxed.
* @throws IOException
* thrown if there is a problem reading the file.
* @throws XmlStreamReaderException
* thrown if the charset encoding could not be determined according to the specs.
*
*/
public XmlReader( InputStream is, String httpContentType, boolean lenient ) throws IOException, XmlStreamReaderException
{
this( is, httpContentType, lenient, null );
}
private void doLenientDetection( String httpContentType, XmlStreamReaderException ex ) throws IOException
{
if ( httpContentType != null )
{
if ( httpContentType.startsWith( "text/html" ) )
{
httpContentType = httpContentType.substring( "text/html".length() );
httpContentType = "text/xml" + httpContentType;
try
{
doHttpStream( ex.getInputStream(), httpContentType, true );
ex = null;
}
catch ( XmlStreamReaderException ex2 )
{
ex = ex2;
}
}
}
if ( ex != null )
{
String encoding = ex.getXmlEncoding();
if ( encoding == null )
{
encoding = ex.getContentTypeEncoding();
}
if ( encoding == null )
{
encoding = ( _defaultEncoding == null ) ? UTF_8 : _defaultEncoding;
}
prepareReader( ex.getInputStream(), encoding );
}
}
/**
* Returns the charset encoding of the XmlReader.
*
*
* @return charset encoding.
*
*/
public String getEncoding()
{
return _encoding;
}
public int read( char[] buf, int offset, int len ) throws IOException
{
return _reader.read( buf, offset, len );
}
/**
* Closes the XmlReader stream.
*
*
* @throws IOException
* thrown if there was a problem closing the stream.
*
*/
public void close() throws IOException
{
_reader.close();
}
private void doRawStream( InputStream is, boolean lenient ) throws IOException
{
BufferedInputStream pis = new BufferedInputStream( is, BUFFER_SIZE );
String bomEnc = getBOMEncoding( pis );
String xmlGuessEnc = getXMLGuessEncoding( pis );
String xmlEnc = getXmlProlog( pis, xmlGuessEnc );
String encoding = calculateRawEncoding( bomEnc, xmlGuessEnc, xmlEnc, pis );
prepareReader( pis, encoding );
}
private void doHttpStream( InputStream is, String httpContentType, boolean lenient ) throws IOException
{
BufferedInputStream pis = new BufferedInputStream( is, BUFFER_SIZE );
String cTMime = getContentTypeMime( httpContentType );
String cTEnc = getContentTypeEncoding( httpContentType );
String bomEnc = getBOMEncoding( pis );
String xmlGuessEnc = getXMLGuessEncoding( pis );
String xmlEnc = getXmlProlog( pis, xmlGuessEnc );
String encoding = calculateHttpEncoding( cTMime, cTEnc, bomEnc, xmlGuessEnc, xmlEnc, pis, lenient );
prepareReader( pis, encoding );
}
private void prepareReader( InputStream is, String encoding ) throws IOException
{
_reader = new InputStreamReader( is, encoding );
_encoding = encoding;
}
// InputStream is passed for XmlStreamReaderException creation only
private String calculateRawEncoding( String bomEnc, String xmlGuessEnc, String xmlEnc, InputStream is )
throws IOException
{
String encoding;
if ( bomEnc == null )
{
if ( xmlGuessEnc == null || xmlEnc == null )
{
encoding = ( _defaultEncoding == null ) ? UTF_8 : _defaultEncoding;
}
else if ( xmlEnc.equals( UTF_16 ) && ( xmlGuessEnc.equals( UTF_16BE ) || xmlGuessEnc.equals( UTF_16LE ) ) )
{
encoding = xmlGuessEnc;
}
else
{
encoding = xmlEnc;
}
}
else if ( bomEnc.equals( UTF_8 ) )
{
if ( xmlGuessEnc != null && !xmlGuessEnc.equals( UTF_8 ) )
{
throw new XmlStreamReaderException( RAW_EX_1.format( new Object[] { bomEnc, xmlGuessEnc, xmlEnc } ), bomEnc,
xmlGuessEnc, xmlEnc, is );
}
if ( xmlEnc != null && !xmlEnc.equals( UTF_8 ) )
{
throw new XmlStreamReaderException( RAW_EX_1.format( new Object[] { bomEnc, xmlGuessEnc, xmlEnc } ), bomEnc,
xmlGuessEnc, xmlEnc, is );
}
encoding = UTF_8;
}
else if ( bomEnc.equals( UTF_16BE ) || bomEnc.equals( UTF_16LE ) )
{
if ( xmlGuessEnc != null && !xmlGuessEnc.equals( bomEnc ) )
{
throw new IOException( RAW_EX_1.format( new Object[] { bomEnc, xmlGuessEnc, xmlEnc } ) );
}
if ( xmlEnc != null && !xmlEnc.equals( UTF_16 ) && !xmlEnc.equals( bomEnc ) )
{
throw new XmlStreamReaderException( RAW_EX_1.format( new Object[] { bomEnc, xmlGuessEnc, xmlEnc } ), bomEnc,
xmlGuessEnc, xmlEnc, is );
}
encoding = bomEnc;
}
else
{
throw new XmlStreamReaderException( RAW_EX_2.format( new Object[] { bomEnc, xmlGuessEnc, xmlEnc } ), bomEnc,
xmlGuessEnc, xmlEnc, is );
}
return encoding;
}
// InputStream is passed for XmlStreamReaderException creation only
private String calculateHttpEncoding( String cTMime, String cTEnc, String bomEnc, String xmlGuessEnc,
String xmlEnc, InputStream is, boolean lenient ) throws IOException
{
String encoding;
if ( lenient & xmlEnc != null )
{
encoding = xmlEnc;
}
else
{
boolean appXml = isAppXml( cTMime );
boolean textXml = isTextXml( cTMime );
if ( appXml || textXml )
{
if ( cTEnc == null )
{
if ( appXml )
{
encoding = calculateRawEncoding( bomEnc, xmlGuessEnc, xmlEnc, is );
}
else
{
encoding = ( _defaultEncoding == null ) ? US_ASCII : _defaultEncoding;
}
}
else if ( bomEnc != null && ( cTEnc.equals( UTF_16BE ) || cTEnc.equals( UTF_16LE ) ) )
{
throw new XmlStreamReaderException( HTTP_EX_1.format( new Object[] { cTMime, cTEnc, bomEnc, xmlGuessEnc,
xmlEnc } ), cTMime, cTEnc, bomEnc, xmlGuessEnc, xmlEnc, is );
}
else if ( cTEnc.equals( UTF_16 ) )
{
if ( bomEnc != null && bomEnc.startsWith( UTF_16 ) )
{
encoding = bomEnc;
}
else
{
throw new XmlStreamReaderException( HTTP_EX_2.format( new Object[] { cTMime, cTEnc, bomEnc,
xmlGuessEnc, xmlEnc } ), cTMime, cTEnc, bomEnc, xmlGuessEnc, xmlEnc, is );
}
}
else
{
encoding = cTEnc;
}
}
else
{
throw new XmlStreamReaderException( HTTP_EX_3.format( new Object[] { cTMime, cTEnc, bomEnc, xmlGuessEnc,
xmlEnc } ), cTMime, cTEnc, bomEnc, xmlGuessEnc, xmlEnc, is );
}
}
return encoding;
}
// returns MIME type or NULL if httpContentType is NULL
private static String getContentTypeMime( String httpContentType )
{
String mime = null;
if ( httpContentType != null )
{
int i = httpContentType.indexOf( ";" );
mime = ( ( i == -1 ) ? httpContentType : httpContentType.substring( 0, i ) ).trim();
}
return mime;
}
private static final Pattern CHARSET_PATTERN = Pattern.compile( "charset=([.[^; ]]*)" );
// returns charset parameter value, NULL if not present, NULL if httpContentType is NULL
private static String getContentTypeEncoding( String httpContentType )
{
String encoding = null;
if ( httpContentType != null )
{
int i = httpContentType.indexOf( ";" );
if ( i > -1 )
{
String postMime = httpContentType.substring( i + 1 );
Matcher m = CHARSET_PATTERN.matcher( postMime );
encoding = ( m.find() ) ? m.group( 1 ) : null;
encoding = ( encoding != null ) ? encoding.toUpperCase( Locale.ENGLISH ) : null;
}
}
return encoding;
}
// returns the BOM in the stream, NULL if not present,
// if there was BOM the in the stream it is consumed
private static String getBOMEncoding( BufferedInputStream is ) throws IOException
{
String encoding = null;
int[] bytes = new int[3];
is.mark( 3 );
bytes[0] = is.read();
bytes[1] = is.read();
bytes[2] = is.read();
if ( bytes[0] == 0xFE && bytes[1] == 0xFF )
{
encoding = UTF_16BE;
is.reset();
is.read();
is.read();
}
else if ( bytes[0] == 0xFF && bytes[1] == 0xFE )
{
encoding = UTF_16LE;
is.reset();
is.read();
is.read();
}
else if ( bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF )
{
encoding = UTF_8;
}
else
{
is.reset();
}
return encoding;
}
// returns the best guess for the encoding by looking the first bytes of the stream, ''
private static String getXMLGuessEncoding( BufferedInputStream is ) throws IOException
{
String encoding = null;
int[] bytes = new int[4];
is.mark( 4 );
bytes[0] = is.read();
bytes[1] = is.read();
bytes[2] = is.read();
bytes[3] = is.read();
is.reset();
if ( bytes[0] == 0x00 && bytes[1] == 0x3C && bytes[2] == 0x00 && bytes[3] == 0x3F )
{
encoding = UTF_16BE;
}
else if ( bytes[0] == 0x3C && bytes[1] == 0x00 && bytes[2] == 0x3F && bytes[3] == 0x00 )
{
encoding = UTF_16LE;
}
else if ( bytes[0] == 0x3C && bytes[1] == 0x3F && bytes[2] == 0x78 && bytes[3] == 0x6D )
{
encoding = UTF_8;
}
else if ( bytes[0] == 0x4C && bytes[1] == 0x6F && bytes[2] == 0xA7 && bytes[3] == 0x94 )
{
encoding = EBCDIC;
}
return encoding;
}
static final Pattern ENCODING_PATTERN =
Pattern.compile( "<\\?xml.*encoding[\\s]*=[\\s]*((?:\".[^\"]*\")|(?:'.[^']*'))", Pattern.MULTILINE );
// returns the encoding declared in the , NULL if none
private static String getXmlProlog( BufferedInputStream is, String guessedEnc ) throws IOException
{
String encoding = null;
if ( guessedEnc != null )
{
byte[] bytes = new byte[BUFFER_SIZE];
is.mark( BUFFER_SIZE );
int offset = 0;
int max = BUFFER_SIZE;
int c = is.read( bytes, offset, max );
int firstGT = -1;
String xmlProlog = null;
while ( c != -1 && firstGT == -1 && offset < BUFFER_SIZE )
{
offset += c;
max -= c;
c = is.read( bytes, offset, max );
xmlProlog = new String( bytes, 0, offset, guessedEnc );
firstGT = xmlProlog.indexOf( '>' );
}
if ( firstGT == -1 )
{
if ( c == -1 )
{
throw new IOException( "Unexpected end of XML stream" );
}
else
{
throw new IOException( "XML prolog or ROOT element not found on first " + offset + " bytes" );
}
}
int bytesRead = offset;
if ( bytesRead > 0 )
{
is.reset();
BufferedReader bReader = new BufferedReader( new StringReader( xmlProlog.substring( 0, firstGT + 1 ) ) );
StringBuffer prolog = new StringBuffer();
String line = bReader.readLine();
while ( line != null )
{
prolog.append( line );
line = bReader.readLine();
}
Matcher m = ENCODING_PATTERN.matcher( prolog );
if ( m.find() )
{
encoding = m.group( 1 ).toUpperCase( Locale.ENGLISH );
encoding = encoding.substring( 1, encoding.length() - 1 );
}
}
}
return encoding;
}
// indicates if the MIME type belongs to the APPLICATION XML family
private static boolean isAppXml( String mime )
{
return mime != null
&& ( mime.equals( "application/xml" ) || mime.equals( "application/xml-dtd" )
|| mime.equals( "application/xml-external-parsed-entity" ) || ( mime.startsWith( "application/" ) && mime.endsWith( "+xml" ) ) );
}
// indicates if the MIME type belongs to the TEXT XML family
private static boolean isTextXml( String mime )
{
return mime != null
&& ( mime.equals( "text/xml" ) || mime.equals( "text/xml-external-parsed-entity" ) || ( mime.startsWith( "text/" ) && mime.endsWith( "+xml" ) ) );
}
private static final MessageFormat RAW_EX_1 =
new MessageFormat( "Invalid encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] encoding mismatch" );
private static final MessageFormat RAW_EX_2 =
new MessageFormat( "Invalid encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] unknown BOM" );
private static final MessageFormat HTTP_EX_1 =
new MessageFormat(
"Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], BOM must be NULL" );
private static final MessageFormat HTTP_EX_2 =
new MessageFormat(
"Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], encoding mismatch" );
private static final MessageFormat HTTP_EX_3 =
new MessageFormat(
"Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], Invalid MIME" );
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/XmlReaderException.java 0000664 0000000 0000000 00000012061 11660305702 0030776 0 ustar 00root root 0000000 0000000 /*
* Copyright 2004 Sun Microsystems, 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 org.codehaus.plexus.util.xml;
import java.io.InputStream;
import java.io.IOException;
/**
* The XmlReaderException is thrown by the XmlReader constructors if the charset encoding can not be determined
* according to the XML 1.0 specification and RFC 3023.
*
* The exception returns the unconsumed InputStream to allow the application to do an alternate processing with the
* stream. Note that the original InputStream given to the XmlReader cannot be used as that one has been already read.
*
*
* @author Alejandro Abdelnur
* @version revision 1.1 taken on 26/06/2007 from Rome (see https://rome.dev.java.net/source/browse/rome/src/java/com/sun/syndication/io/XmlReaderException.java)
*/
public class XmlReaderException extends IOException
{
private String _bomEncoding;
private String _xmlGuessEncoding;
private String _xmlEncoding;
private String _contentTypeMime;
private String _contentTypeEncoding;
private InputStream _is;
/**
* Creates an exception instance if the charset encoding could not be determined.
*
* Instances of this exception are thrown by the XmlReader.
*
*
* @param msg
* message describing the reason for the exception.
* @param bomEnc
* BOM encoding.
* @param xmlGuessEnc
* XML guess encoding.
* @param xmlEnc
* XML prolog encoding.
* @param is
* the unconsumed InputStream.
*
*/
public XmlReaderException( String msg, String bomEnc, String xmlGuessEnc, String xmlEnc, InputStream is )
{
this( msg, null, null, bomEnc, xmlGuessEnc, xmlEnc, is );
}
/**
* Creates an exception instance if the charset encoding could not be determined.
*
* Instances of this exception are thrown by the XmlReader.
*
*
* @param msg
* message describing the reason for the exception.
* @param ctMime
* MIME type in the content-type.
* @param ctEnc
* encoding in the content-type.
* @param bomEnc
* BOM encoding.
* @param xmlGuessEnc
* XML guess encoding.
* @param xmlEnc
* XML prolog encoding.
* @param is
* the unconsumed InputStream.
*
*/
public XmlReaderException( String msg, String ctMime, String ctEnc, String bomEnc, String xmlGuessEnc,
String xmlEnc, InputStream is )
{
super( msg );
_contentTypeMime = ctMime;
_contentTypeEncoding = ctEnc;
_bomEncoding = bomEnc;
_xmlGuessEncoding = xmlGuessEnc;
_xmlEncoding = xmlEnc;
_is = is;
}
/**
* Returns the BOM encoding found in the InputStream.
*
*
* @return the BOM encoding, null if none.
*
*/
public String getBomEncoding()
{
return _bomEncoding;
}
/**
* Returns the encoding guess based on the first bytes of the InputStream.
*
*
* @return the encoding guess, null if it couldn't be guessed.
*
*/
public String getXmlGuessEncoding()
{
return _xmlGuessEncoding;
}
/**
* Returns the encoding found in the XML prolog of the InputStream.
*
*
* @return the encoding of the XML prolog, null if none.
*
*/
public String getXmlEncoding()
{
return _xmlEncoding;
}
/**
* Returns the MIME type in the content-type used to attempt determining the encoding.
*
*
* @return the MIME type in the content-type, null if there was not content-type or the encoding detection did not
* involve HTTP.
*
*/
public String getContentTypeMime()
{
return _contentTypeMime;
}
/**
* Returns the encoding in the content-type used to attempt determining the encoding.
*
*
* @return the encoding in the content-type, null if there was not content-type, no encoding in it or the encoding
* detection did not involve HTTP.
*
*/
public String getContentTypeEncoding()
{
return _contentTypeEncoding;
}
/**
* Returns the unconsumed InputStream to allow the application to do an alternate encoding detection on the
* InputStream.
*
*
* @return the unconsumed InputStream.
*
*/
public InputStream getInputStream()
{
return _is;
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/XmlStreamReader.java 0000664 0000000 0000000 00000025164 11660305702 0030303 0 ustar 00root root 0000000 0000000 /*
* Copyright 2004 Sun Microsystems, 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 org.codehaus.plexus.util.xml;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
/**
* Character stream that handles (or at least attemtps to) all the necessary Voodo to figure out the charset encoding of
* the XML document within the stream.
*
* IMPORTANT: This class is not related in any way to the org.xml.sax.XMLReader. This one IS a character stream.
*
* All this has to be done without consuming characters from the stream, if not the XML parser will not recognized the
* document as a valid XML. This is not 100% true, but it's close enough (UTF-8 BOM is not handled by all parsers right
* now, XmlReader handles it and things work in all parsers).
*
* The XmlReader class handles the charset encoding of XML documents in Files, raw streams and HTTP streams by offering
* a wide set of constructors.
*
* By default the charset encoding detection is lenient, the constructor with the lenient flag can be used for an script
* (following HTTP MIME and XML specifications). All this is nicely explained by Mark Pilgrim in his blog, Determining the character encoding of a feed.
*
*
* @author Alejandro Abdelnur
* @version revision 1.17 taken on 26/06/2007 from Rome (see https://rome.dev.java.net/source/browse/rome/src/java/com/sun/syndication/io/XmlReader.java)
* @since 1.4.4
*/
public class XmlStreamReader
extends XmlReader
{
/**
* Creates a Reader for a File.
*
* It looks for the UTF-8 BOM first, if none sniffs the XML prolog charset, if this is also missing defaults to
* UTF-8.
*
* It does a lenient charset encoding detection, check the constructor with the lenient parameter for details.
*
*
* @param file
* File to create a Reader from.
* @throws IOException
* thrown if there is a problem reading the file.
*
*/
public XmlStreamReader( File file ) throws IOException
{
super( file );
}
/**
* Creates a Reader for a raw InputStream.
*
* It follows the same logic used for files.
*
* It does a lenient charset encoding detection, check the constructor with the lenient parameter for details.
*
*
* @param is
* InputStream to create a Reader from.
* @throws IOException
* thrown if there is a problem reading the stream.
*
*/
public XmlStreamReader( InputStream is ) throws IOException
{
super( is );
}
/**
* Creates a Reader for a raw InputStream.
*
* It follows the same logic used for files.
*
* If lenient detection is indicated and the detection above fails as per specifications it then attempts the
* following:
*
* If the content type was 'text/html' it replaces it with 'text/xml' and tries the detection again.
*
* Else if the XML prolog had a charset encoding that encoding is used.
*
* Else if the content type had a charset encoding that encoding is used.
*
* Else 'UTF-8' is used.
*
* If lenient detection is indicated an XmlStreamReaderException is never thrown.
*
*
* @param is
* InputStream to create a Reader from.
* @param lenient
* indicates if the charset encoding detection should be relaxed.
* @throws IOException
* thrown if there is a problem reading the stream.
* @throws XmlStreamReaderException
* thrown if the charset encoding could not be determined according to the specs.
*
*/
public XmlStreamReader( InputStream is, boolean lenient ) throws IOException, XmlStreamReaderException
{
super( is, lenient );
}
/**
* Creates a Reader using the InputStream of a URL.
*
* If the URL is not of type HTTP and there is not 'content-type' header in the fetched data it uses the same logic
* used for Files.
*
* If the URL is a HTTP Url or there is a 'content-type' header in the fetched data it uses the same logic used for
* an InputStream with content-type.
*
* It does a lenient charset encoding detection, check the constructor with the lenient parameter for details.
*
*
* @param url
* URL to create a Reader from.
* @throws IOException
* thrown if there is a problem reading the stream of the URL.
*
*/
public XmlStreamReader( URL url ) throws IOException
{
super( url );
}
/**
* Creates a Reader using the InputStream of a URLConnection.
*
* If the URLConnection is not of type HttpURLConnection and there is not 'content-type' header in the fetched data
* it uses the same logic used for files.
*
* If the URLConnection is a HTTP Url or there is a 'content-type' header in the fetched data it uses the same logic
* used for an InputStream with content-type.
*
* It does a lenient charset encoding detection, check the constructor with the lenient parameter for details.
*
*
* @param conn
* URLConnection to create a Reader from.
* @throws IOException
* thrown if there is a problem reading the stream of the URLConnection.
*
*/
public XmlStreamReader( URLConnection conn ) throws IOException
{
super( conn );
}
/**
* Creates a Reader using an InputStream an the associated content-type header.
*
* First it checks if the stream has BOM. If there is not BOM checks the content-type encoding. If there is not
* content-type encoding checks the XML prolog encoding. If there is not XML prolog encoding uses the default
* encoding mandated by the content-type MIME type.
*
* It does a lenient charset encoding detection, check the constructor with the lenient parameter for details.
*
*
* @param is
* InputStream to create the reader from.
* @param httpContentType
* content-type header to use for the resolution of the charset encoding.
* @throws IOException
* thrown if there is a problem reading the file.
*
*/
public XmlStreamReader( InputStream is, String httpContentType ) throws IOException
{
super( is, httpContentType );
}
/**
* Creates a Reader using an InputStream an the associated content-type header. This constructor is lenient
* regarding the encoding detection.
*
* First it checks if the stream has BOM. If there is not BOM checks the content-type encoding. If there is not
* content-type encoding checks the XML prolog encoding. If there is not XML prolog encoding uses the default
* encoding mandated by the content-type MIME type.
*
* If lenient detection is indicated and the detection above fails as per specifications it then attempts the
* following:
*
* If the content type was 'text/html' it replaces it with 'text/xml' and tries the detection again.
*
* Else if the XML prolog had a charset encoding that encoding is used.
*
* Else if the content type had a charset encoding that encoding is used.
*
* Else 'UTF-8' is used.
*
* If lenient detection is indicated an XmlStreamReaderException is never thrown.
*
*
* @param is
* InputStream to create the reader from.
* @param httpContentType
* content-type header to use for the resolution of the charset encoding.
* @param lenient
* indicates if the charset encoding detection should be relaxed.
* @throws IOException
* thrown if there is a problem reading the file.
* @throws XmlStreamReaderException
* thrown if the charset encoding could not be determined according to the specs.
*
*/
public XmlStreamReader( InputStream is, String httpContentType, boolean lenient, String defaultEncoding )
throws IOException, XmlStreamReaderException
{
super( is, httpContentType, lenient, defaultEncoding );
}
/**
* Creates a Reader using an InputStream an the associated content-type header. This constructor is lenient
* regarding the encoding detection.
*
* First it checks if the stream has BOM. If there is not BOM checks the content-type encoding. If there is not
* content-type encoding checks the XML prolog encoding. If there is not XML prolog encoding uses the default
* encoding mandated by the content-type MIME type.
*
* If lenient detection is indicated and the detection above fails as per specifications it then attempts the
* following:
*
* If the content type was 'text/html' it replaces it with 'text/xml' and tries the detection again.
*
* Else if the XML prolog had a charset encoding that encoding is used.
*
* Else if the content type had a charset encoding that encoding is used.
*
* Else 'UTF-8' is used.
*
* If lenient detection is indicated an XmlStreamReaderException is never thrown.
*
*
* @param is
* InputStream to create the reader from.
* @param httpContentType
* content-type header to use for the resolution of the charset encoding.
* @param lenient
* indicates if the charset encoding detection should be relaxed.
* @throws IOException
* thrown if there is a problem reading the file.
* @throws XmlStreamReaderException
* thrown if the charset encoding could not be determined according to the specs.
*
*/
public XmlStreamReader( InputStream is, String httpContentType, boolean lenient ) throws IOException, XmlStreamReaderException
{
super( is, httpContentType, lenient );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/XmlStreamReaderException.java 0000664 0000000 0000000 00000005712 11660305702 0032157 0 ustar 00root root 0000000 0000000 /*
* Copyright 2004 Sun Microsystems, 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 org.codehaus.plexus.util.xml;
import java.io.InputStream;
/**
* The XmlStreamReaderException is thrown by the XmlStreamReader constructors if the charset encoding can not be determined
* according to the XML 1.0 specification and RFC 3023.
*
* The exception returns the unconsumed InputStream to allow the application to do an alternate processing with the
* stream. Note that the original InputStream given to the XmlStreamReader cannot be used as that one has been already read.
*
*
* @author Alejandro Abdelnur
* @version revision 1.1 taken on 26/06/2007 from Rome (see https://rome.dev.java.net/source/browse/rome/src/java/com/sun/syndication/io/XmlReaderException.java)
*/
public class XmlStreamReaderException extends XmlReaderException
{
/**
* Creates an exception instance if the charset encoding could not be determined.
*
* Instances of this exception are thrown by the XmlReader.
*
*
* @param msg
* message describing the reason for the exception.
* @param bomEnc
* BOM encoding.
* @param xmlGuessEnc
* XML guess encoding.
* @param xmlEnc
* XML prolog encoding.
* @param is
* the unconsumed InputStream.
*
*/
public XmlStreamReaderException( String msg, String bomEnc, String xmlGuessEnc, String xmlEnc, InputStream is )
{
super( msg, bomEnc, xmlGuessEnc, xmlEnc, is );
}
/**
* Creates an exception instance if the charset encoding could not be determined.
*
* Instances of this exception are thrown by the XmlReader.
*
*
* @param msg
* message describing the reason for the exception.
* @param ctMime
* MIME type in the content-type.
* @param ctEnc
* encoding in the content-type.
* @param bomEnc
* BOM encoding.
* @param xmlGuessEnc
* XML guess encoding.
* @param xmlEnc
* XML prolog encoding.
* @param is
* the unconsumed InputStream.
*
*/
public XmlStreamReaderException( String msg, String ctMime, String ctEnc, String bomEnc, String xmlGuessEnc,
String xmlEnc, InputStream is )
{
super( msg, ctMime, ctEnc, bomEnc, xmlGuessEnc, xmlEnc, is );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/XmlStreamWriter.java 0000664 0000000 0000000 00000011337 11660305702 0030352 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.xml;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Character stream that handles (or at least attemtps to) all the necessary Voodo to figure out the charset encoding of
* the XML document written to the stream.
* @author Herve Boutemy
* @version $Id: XmlStreamWriter.java 8010 2009-01-07 12:59:50Z vsiveton $
* @since 1.4.4
*/
public class XmlStreamWriter
extends Writer
{
private static final int BUFFER_SIZE = 4096;
private StringWriter xmlPrologWriter = new StringWriter( BUFFER_SIZE );
private OutputStream out;
private Writer writer;
private String encoding;
public XmlStreamWriter( OutputStream out )
{
this.out = out;
}
public XmlStreamWriter( File file )
throws FileNotFoundException
{
this( new FileOutputStream( file ) );
}
public String getEncoding()
{
return encoding;
}
public void close()
throws IOException
{
if ( writer == null )
{
encoding = "UTF-8";
writer = new OutputStreamWriter( out, encoding );
writer.write( xmlPrologWriter.toString() );
}
writer.close();
}
public void flush()
throws IOException
{
if ( writer != null )
{
writer.flush();
}
}
private void detectEncoding( char[] cbuf, int off, int len )
throws IOException
{
int size = len;
StringBuffer xmlProlog = xmlPrologWriter.getBuffer();
if ( xmlProlog.length() + len > BUFFER_SIZE )
{
size = BUFFER_SIZE - xmlProlog.length();
}
xmlPrologWriter.write( cbuf, off, size );
// try to determine encoding
if ( xmlProlog.length() >= 5 )
{
if ( xmlProlog.substring( 0, 5 ).equals( "" );
if ( xmlPrologEnd > 0 )
{
// ok, full XML prolog written: let's extract encoding
Matcher m = ENCODING_PATTERN.matcher( xmlProlog.substring( 0, xmlPrologEnd ) );
if ( m.find() )
{
encoding = m.group( 1 ).toUpperCase( Locale.ENGLISH );
encoding = encoding.substring( 1, encoding.length() - 1 );
}
else
{
// no encoding found in XML prolog: using default encoding
encoding = "UTF-8";
}
}
else
{
if ( xmlProlog.length() >= BUFFER_SIZE )
{
// no encoding found in first characters: using default encoding
encoding = "UTF-8";
}
}
}
else
{
// no XML prolog: using default encoding
encoding = "UTF-8";
}
if ( encoding != null )
{
// encoding has been chosen: let's do it
xmlPrologWriter = null;
writer = new OutputStreamWriter( out, encoding );
writer.write( xmlProlog.toString() );
if ( len > size )
{
writer.write( cbuf, off + size, len - size );
}
}
}
}
public void write( char[] cbuf, int off, int len )
throws IOException
{
if ( xmlPrologWriter != null )
{
detectEncoding( cbuf, off, len );
}
else
{
writer.write( cbuf, off, len );
}
}
static final Pattern ENCODING_PATTERN = XmlReader.ENCODING_PATTERN;
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/XmlUtil.java 0000664 0000000 0000000 00000024671 11660305702 0026644 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.xml;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.WriterFactory;
import org.codehaus.plexus.util.xml.pull.MXParser;
import org.codehaus.plexus.util.xml.pull.XmlPullParser;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
/**
* Common XML utilities methods.
*
* @author Vincent Siveton
* @version $Id: XmlUtil.java 8010 2009-01-07 12:59:50Z vsiveton $
* @since 1.5.7
*/
public class XmlUtil
{
/** The default line indenter size i.e. 2. */
public static final int DEFAULT_INDENTATION_SIZE = 2;
/** The default line separator ("\n" on UNIX) */
public static final String DEFAULT_LINE_SEPARATOR = System.getProperty( "line.separator" );
/**
* Determines if a given File shall be handled as XML.
*
* @param f not null file
* @return true
if the given file has XML content, false
otherwise.
*/
public static boolean isXml( File f )
{
if ( f == null )
{
throw new IllegalArgumentException( "f could not be null." );
}
if ( !f.isFile() )
{
throw new IllegalArgumentException( "The file '" + f.getAbsolutePath() + "' is not a file." );
}
Reader reader = null;
try
{
reader = ReaderFactory.newXmlReader( f );
XmlPullParser parser = new MXParser();
parser.setInput( reader );
parser.nextToken();
return true;
}
catch ( Exception e )
{
return false;
}
finally
{
IOUtil.close( reader );
}
}
/**
* Pretty format the input reader. For instance, the following input:
*
* <div><b>content</b></div>
*
* becomes
*
* <div>
* <b>content</b>
* </div>
*
*
* @param reader not null
* @param writer not null
* @throws IOException if any or invalid xml content
* @see #prettyFormat(Reader, Writer, int, String)
* @see ReaderFactory to read an xml content
* @see WriterFactory to write an xml content
*/
public static void prettyFormat( Reader reader, Writer writer )
throws IOException
{
prettyFormat( reader, writer, DEFAULT_INDENTATION_SIZE, DEFAULT_LINE_SEPARATOR );
}
/**
* Pretty format the input reader. For instance, the following input:
*
* <div><b>content</b></div>
*
* becomes
*
* <div>
* <b>content</b>
* </div>
*
*
* @param reader not null
* @param writer not null
* @param indentSize positive number for the indentation
* @param lineSeparator the wanted line separator
* @throws IOException if any or invalid xml content
* @see ReaderFactory to read an xml content
* @see WriterFactory to write an xml content
*/
public static void prettyFormat( Reader reader, Writer writer, int indentSize, String lineSeparator )
throws IOException
{
if ( reader == null )
{
throw new IllegalArgumentException( "The reader is null" );
}
if ( writer == null )
{
throw new IllegalArgumentException( "The writer is null" );
}
if ( indentSize < 0 )
{
indentSize = 0;
}
PrettyPrintXMLWriter xmlWriter = new PrettyPrintXMLWriter( writer );
xmlWriter.setLineIndenter( StringUtils.repeat( " ", indentSize ) );
xmlWriter.setLineSeparator( lineSeparator );
XmlPullParser parser = new MXParser();
try
{
parser.setInput( reader );
prettyFormatInternal( parser, xmlWriter );
}
catch ( XmlPullParserException e )
{
throw new IOException( "Unable to parse the XML: " + e.getMessage() );
}
}
/**
* Pretty format the input stream. For instance, the following input:
*
* <div><b>content</b></div>
*
* becomes
*
* <div>
* <b>content</b>
* </div>
*
*
* @param is not null
* @param os not null
* @throws IOException if any or invalid xml content
* @see #prettyFormat(InputStream, OutputStream, int, String)
*/
public static void prettyFormat( InputStream is, OutputStream os )
throws IOException
{
prettyFormat( is, os, DEFAULT_INDENTATION_SIZE, DEFAULT_LINE_SEPARATOR );
}
/**
* Pretty format the input stream. For instance, the following input:
*
* <div><b>content</b></div>
*
* becomes
*
* <div>
* <b>content</b>
* </div>
*
*
* @param is not null
* @param os not null
* @param indentSize positive number for the indentation
* @param lineSeparator the wanted line separator
* @throws IOException if any or invalid xml content
*/
public static void prettyFormat( InputStream is, OutputStream os, int indentSize, String lineSeparator )
throws IOException
{
if ( is == null )
{
throw new IllegalArgumentException( "The is is null" );
}
if ( os == null )
{
throw new IllegalArgumentException( "The os is null" );
}
if ( indentSize < 0 )
{
indentSize = 0;
}
Reader reader = null;
Writer out = new OutputStreamWriter( os );
PrettyPrintXMLWriter xmlWriter = new PrettyPrintXMLWriter( out );
xmlWriter.setLineIndenter( StringUtils.repeat( " ", indentSize ) );
xmlWriter.setLineSeparator( lineSeparator );
XmlPullParser parser = new MXParser();
try
{
reader = ReaderFactory.newXmlReader( is );
parser.setInput( reader );
prettyFormatInternal( parser, xmlWriter );
}
catch ( XmlPullParserException e )
{
throw new IOException( "Unable to parse the XML: " + e.getMessage() );
}
finally
{
IOUtil.close( reader );
IOUtil.close( out );
}
}
/**
* @param parser not null
* @param writer not null
* @throws XmlPullParserException if any
* @throws IOException if any
*/
private static void prettyFormatInternal( XmlPullParser parser, PrettyPrintXMLWriter writer )
throws XmlPullParserException, IOException
{
boolean hasTag = false;
boolean hasComment = false;
int eventType = parser.getEventType();
while ( eventType != XmlPullParser.END_DOCUMENT )
{
if ( eventType == XmlPullParser.START_TAG )
{
hasTag = true;
if ( hasComment )
{
writer.writeText( writer.getLineIndenter() );
hasComment = false;
}
writer.startElement( parser.getName() );
for ( int i = 0; i < parser.getAttributeCount(); i++ )
{
String key = parser.getAttributeName( i );
String value = parser.getAttributeValue( i );
writer.addAttribute( key, value );
}
}
else if ( eventType == XmlPullParser.TEXT )
{
String text = parser.getText();
if ( !text.trim().equals( "" ) )
{
text = StringUtils.removeDuplicateWhitespace( text );
writer.writeText( text );
}
}
else if ( eventType == XmlPullParser.END_TAG )
{
hasTag = false;
writer.endElement();
}
else if ( eventType == XmlPullParser.COMMENT )
{
hasComment = true;
if ( !hasTag )
{
writer.writeMarkup( writer.getLineSeparator() );
for ( int i = 0; i < writer.getDepth(); i++ )
{
writer.writeMarkup( writer.getLineIndenter() );
}
}
writer.writeMarkup( "" );
if ( !hasTag )
{
writer.writeMarkup( writer.getLineSeparator() );
for ( int i = 0; i < writer.getDepth() - 1; i++ )
{
writer.writeMarkup( writer.getLineIndenter() );
}
}
}
else if ( eventType == XmlPullParser.DOCDECL )
{
writer.writeMarkup( "" );
writer.endOfLine();
}
else if ( eventType == XmlPullParser.PROCESSING_INSTRUCTION )
{
writer.writeMarkup( "" + parser.getText() + "?>" );
writer.endOfLine();
}
else if ( eventType == XmlPullParser.CDSECT )
{
writer.writeMarkup( "" );
}
else if ( eventType == XmlPullParser.ENTITY_REF )
{
writer.writeMarkup( "&" + parser.getName() + ";" );
}
eventType = parser.nextToken();
}
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/XmlWriterUtil.java 0000664 0000000 0000000 00000025615 11660305702 0030040 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.xml;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import org.codehaus.plexus.util.StringUtils;
/**
* Utility class for the XmlWriter
class.
*
* @author Vincent Siveton
* @version $Id: XmlWriterUtil.java 8660 2010-03-05 21:50:36Z hboutemy $
*/
public class XmlWriterUtil
{
/** The vm line separator */
public static final String LS = System.getProperty( "line.separator" );
/** The default line indenter size i.e. 2. */
public static final int DEFAULT_INDENTATION_SIZE = 2;
/** The default column before line wrapping i.e. 80. */
public static final int DEFAULT_COLUMN_LINE = 80;
/**
* Convenience method to write one CRLF
.
*
* @param writer not null writer
*/
public static void writeLineBreak( XMLWriter writer )
{
writeLineBreak( writer, 1 );
}
/**
* Convenience method to repeat CRLF
.
*
* @param writer not null
* @param repeat positive number
*/
public static void writeLineBreak( XMLWriter writer, int repeat )
{
for ( int i = 0; i < repeat; i++ )
{
writer.writeMarkup( LS );
}
}
/**
* Convenience method to repeat CRLF
and to indent the writer by 2
.
*
* @param writer not null
* @param repeat
* @param indent positive number
* @see #DEFAULT_INDENTATION_SIZE
* @see #writeLineBreak(XMLWriter, int, int, int)
*/
public static void writeLineBreak( XMLWriter writer, int repeat, int indent )
{
writeLineBreak( writer, repeat, indent, DEFAULT_INDENTATION_SIZE );
}
/**
* Convenience method to repeat CRLF
and to indent the writer by indentSize
.
*
* @param writer not null
* @param repeat
* @param indent positive number
* @param indentSize positive number
*/
public static void writeLineBreak( XMLWriter writer, int repeat, int indent, int indentSize )
{
writeLineBreak( writer, repeat );
if ( indent < 0 )
{
indent = 0;
}
if ( indentSize < 0 )
{
indentSize = 0;
}
writer.writeText( StringUtils.repeat( " ", indent * indentSize ) );
}
/**
* Convenience method to write XML comment line break. Its size is 80
.
*
* @param writer not null
* @see #DEFAULT_COLUMN_LINE
* @see #writeCommentLineBreak(XMLWriter, int)
*/
public static void writeCommentLineBreak( XMLWriter writer )
{
writeCommentLineBreak( writer, DEFAULT_COLUMN_LINE );
}
/**
* Convenience method to write XML comment line break with columnSize
as length.
*
* @param writer not null
* @param columnSize positive number
*/
public static void writeCommentLineBreak( XMLWriter writer, int columnSize )
{
if ( columnSize < 10 )
{
columnSize = DEFAULT_COLUMN_LINE;
}
writer.writeMarkup( "" + LS );
}
/**
* Convenience method to write XML comment line. The comment
is splitted to have a size of 80
.
*
* @param writer not null
* @param comment
* @see #DEFAULT_INDENTATION_SIZE
* @see #writeComment(XMLWriter, String, int, int)
*/
public static void writeComment( XMLWriter writer, String comment )
{
writeComment( writer, comment, 0, DEFAULT_INDENTATION_SIZE );
}
/**
* Convenience method to write XML comment line. The comment
is splitted to have a size of 80
* and is indented by indent
using 2
as indentation size.
*
* @param writer not null
* @param comment
* @param indent positive number
* @see #DEFAULT_INDENTATION_SIZE
* @see #writeComment(XMLWriter, String, int, int)
*/
public static void writeComment( XMLWriter writer, String comment, int indent )
{
writeComment( writer, comment, indent, DEFAULT_INDENTATION_SIZE );
}
/**
* Convenience method to write XML comment line. The comment
is splitted to have a size of 80
* and is indented by indent
using indentSize
.
*
* @param writer not null
* @param comment
* @param indent positive number
* @param indentSize positive number
* @see #DEFAULT_COLUMN_LINE
* @see #writeComment(XMLWriter, String, int, int, int)
*/
public static void writeComment( XMLWriter writer, String comment, int indent, int indentSize )
{
writeComment( writer, comment, indent, indentSize, DEFAULT_COLUMN_LINE );
}
/**
* Convenience method to write XML comment line. The comment
is splitted to have a size of columnSize
* and is indented by indent
using indentSize
.
*
* @param writer not null
* @param comment
* @param indent positive number
* @param indentSize positive number
* @param columnSize positive number
*/
public static void writeComment( XMLWriter writer, String comment, int indent, int indentSize, int columnSize )
{
if ( comment == null )
{
comment = "null";
}
if ( indent < 0 )
{
indent = 0;
}
if ( indentSize < 0 )
{
indentSize = 0;
}
if ( columnSize < 0 )
{
columnSize = DEFAULT_COLUMN_LINE;
}
String indentation = StringUtils.repeat( " ", indent * indentSize );
int magicNumber = indentation.length() + columnSize - "-->".length() - 1;
String[] sentences = StringUtils.split( comment, LS );
StringBuffer line = new StringBuffer( indentation + "" ).append( LS );
writer.writeMarkup( line.toString() );
}
line = new StringBuffer( indentation + "" ).append( LS );
writer.writeMarkup( line.toString() );
}
/**
* Convenience method to write XML comments between two comments line break.
* The XML comment block is not indented.
*
* @param writer not null
* @param comment
* @see #DEFAULT_INDENTATION_SIZE
* @see #writeCommentText(XMLWriter, String, int, int)
*/
public static void writeCommentText( XMLWriter writer, String comment )
{
writeCommentText( writer, comment, 0, DEFAULT_INDENTATION_SIZE );
}
/**
* Convenience method to write XML comments between two comments line break.
* The XML comment block is also indented by indent
using
* 2
as indentation size.
*
* @param writer not null
* @param comment
* @param indent positive number
* @see #DEFAULT_INDENTATION_SIZE
* @see #writeCommentText(XMLWriter, String, int, int)
*/
public static void writeCommentText( XMLWriter writer, String comment, int indent )
{
writeCommentText( writer, comment, indent, DEFAULT_INDENTATION_SIZE );
}
/**
* Convenience method to write XML comment between two comment line break.
* The XML comment block is also indented by indent
using indentSize
.
*
* @param writer not null
* @param comment
* @param indent positive number
* @param indentSize positive number
* @see #DEFAULT_COLUMN_LINE
* @see #writeCommentText(XMLWriter, String, int, int, int)
*/
public static void writeCommentText( XMLWriter writer, String comment, int indent, int indentSize )
{
writeCommentText( writer, comment, indent, indentSize, DEFAULT_COLUMN_LINE );
}
/**
* Convenience method to write XML comments between two comments line break.
* The XML comment block is also indented by indent
using indentSize
.
* The column size could be also be specified.
*
* @param writer not null
* @param comment
* @param indent positive number
* @param indentSize positive number
* @param columnSize positive number
*/
public static void writeCommentText( XMLWriter writer, String comment, int indent, int indentSize, int columnSize )
{
if ( indent < 0 )
{
indent = 0;
}
if ( indentSize < 0 )
{
indentSize = 0;
}
if ( columnSize < 0 )
{
columnSize = DEFAULT_COLUMN_LINE;
}
writeLineBreak( writer, 1 );
writer.writeMarkup( StringUtils.repeat( " ", indent * indentSize ) );
writeCommentLineBreak( writer, columnSize );
writeComment( writer, comment, indent, indentSize, columnSize );
writer.writeMarkup( StringUtils.repeat( " ", indent * indentSize ) );
writeCommentLineBreak( writer, columnSize );
writeLineBreak( writer, 1, indent, indentSize );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java 0000664 0000000 0000000 00000044567 11660305702 0026546 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.xml;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import org.codehaus.plexus.util.xml.pull.XmlSerializer;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @version $Id: Xpp3Dom.java 8680 2010-04-06 20:05:21Z bentmann $
* NOTE: remove all the util code in here when separated, this class should be pure data.
*/
public class Xpp3Dom
{
protected String name;
protected String value;
protected Map attributes;
protected final List childList;
protected final Map childMap;
protected Xpp3Dom parent;
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0];
public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children";
public static final String CHILDREN_COMBINATION_MERGE = "merge";
public static final String CHILDREN_COMBINATION_APPEND = "append";
/**
* This default mode for combining children DOMs during merge means that where element names
* match, the process will try to merge the element data, rather than putting the dominant
* and recessive elements (which share the same element name) as siblings in the resulting
* DOM.
*/
public static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE;
public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self";
public static final String SELF_COMBINATION_OVERRIDE = "override";
public static final String SELF_COMBINATION_MERGE = "merge";
/**
* This default mode for combining a DOM node during merge means that where element names
* match, the process will try to merge the element attributes and values, rather than
* overriding the recessive element completely with the dominant one. This means that
* wherever the dominant element doesn't provide the value or a particular attribute, that
* value or attribute will be set from the recessive DOM node.
*/
public static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE;
public Xpp3Dom( String name )
{
this.name = name;
childList = new ArrayList();
childMap = new HashMap();
}
/**
* Copy constructor.
*/
public Xpp3Dom( Xpp3Dom src )
{
this( src, src.getName() );
}
/**
* Copy constructor with alternative name.
*/
public Xpp3Dom( Xpp3Dom src, String name )
{
this.name = name;
int childCount = src.getChildCount();
childList = new ArrayList( childCount );
childMap = new HashMap( childCount << 1 );
setValue( src.getValue() );
String[] attributeNames = src.getAttributeNames();
for ( int i = 0; i < attributeNames.length; i++ )
{
String attributeName = attributeNames[i];
setAttribute( attributeName, src.getAttribute( attributeName ) );
}
for ( int i = 0; i < childCount; i++ )
{
addChild( new Xpp3Dom( src.getChild( i ) ) );
}
}
// ----------------------------------------------------------------------
// Name handling
// ----------------------------------------------------------------------
public String getName()
{
return name;
}
// ----------------------------------------------------------------------
// Value handling
// ----------------------------------------------------------------------
public String getValue()
{
return value;
}
public void setValue( String value )
{
this.value = value;
}
// ----------------------------------------------------------------------
// Attribute handling
// ----------------------------------------------------------------------
public String[] getAttributeNames()
{
if ( null == attributes || attributes.isEmpty() )
{
return EMPTY_STRING_ARRAY;
}
else
{
return (String[]) attributes.keySet().toArray( new String[attributes.size()] );
}
}
public String getAttribute( String name )
{
return ( null != attributes ) ? (String) attributes.get( name ) : null;
}
/**
* Set the attribute value
* @param name String not null
* @param value String not null
*/
public void setAttribute( String name, String value )
{
if ( null == value ) {
throw new NullPointerException( "Attribute value can not be null" );
}
if ( null == name ) {
throw new NullPointerException( "Attribute name can not be null" );
}
if ( null == attributes )
{
attributes = new HashMap();
}
attributes.put( name, value );
}
// ----------------------------------------------------------------------
// Child handling
// ----------------------------------------------------------------------
public Xpp3Dom getChild( int i )
{
return (Xpp3Dom) childList.get( i );
}
public Xpp3Dom getChild( String name )
{
return (Xpp3Dom) childMap.get( name );
}
public void addChild( Xpp3Dom xpp3Dom )
{
xpp3Dom.setParent( this );
childList.add( xpp3Dom );
childMap.put( xpp3Dom.getName(), xpp3Dom );
}
public Xpp3Dom[] getChildren()
{
if ( null == childList || childList.isEmpty() )
{
return EMPTY_DOM_ARRAY;
}
else
{
return (Xpp3Dom[]) childList.toArray( new Xpp3Dom[childList.size()] );
}
}
public Xpp3Dom[] getChildren( String name )
{
if ( null == childList )
{
return EMPTY_DOM_ARRAY;
}
else
{
ArrayList children = new ArrayList();
int size = childList.size();
for ( int i = 0; i < size; i++ )
{
Xpp3Dom configuration = (Xpp3Dom) childList.get( i );
if ( name.equals( configuration.getName() ) )
{
children.add( configuration );
}
}
return (Xpp3Dom[]) children.toArray( new Xpp3Dom[children.size()] );
}
}
public int getChildCount()
{
if ( null == childList )
{
return 0;
}
return childList.size();
}
public void removeChild( int i )
{
Xpp3Dom child = getChild( i );
childMap.values().remove( child );
childList.remove( i );
// In case of any dangling references
child.setParent( null );
}
// ----------------------------------------------------------------------
// Parent handling
// ----------------------------------------------------------------------
public Xpp3Dom getParent()
{
return parent;
}
public void setParent( Xpp3Dom parent )
{
this.parent = parent;
}
// ----------------------------------------------------------------------
// Helpers
// ----------------------------------------------------------------------
public void writeToSerializer( String namespace, XmlSerializer serializer )
throws IOException
{
// TODO: WARNING! Later versions of plexus-utils psit out an header due to thinking this is a new document - not the desired behaviour!
SerializerXMLWriter xmlWriter = new SerializerXMLWriter( namespace, serializer );
Xpp3DomWriter.write( xmlWriter, this );
if ( xmlWriter.getExceptions().size() > 0 )
{
throw (IOException) xmlWriter.getExceptions().get( 0 );
}
}
/**
* Merges one DOM into another, given a specific algorithm and possible override points for that algorithm.
* The algorithm is as follows:
*
* 1. if the recessive DOM is null, there is nothing to do...return.
*
* 2. Determine whether the dominant node will suppress the recessive one (flag=mergeSelf).
*
* A. retrieve the 'combine.self' attribute on the dominant node, and try to match against 'override'...
* if it matches 'override', then set mergeSelf == false...the dominant node suppresses the recessive
* one completely.
*
* B. otherwise, use the default value for mergeSelf, which is true...this is the same as specifying
* 'combine.self' == 'merge' as an attribute of the dominant root node.
*
* 3. If mergeSelf == true
*
* A. if the dominant root node's value is empty, set it to the recessive root node's value
*
* B. For each attribute in the recessive root node which is not set in the dominant root node, set it.
*
* C. Determine whether children from the recessive DOM will be merged or appended to the dominant
* DOM as siblings (flag=mergeChildren).
*
* i. if childMergeOverride is set (non-null), use that value (true/false)
*
* ii. retrieve the 'combine.children' attribute on the dominant node, and try to match against
* 'append'...if it matches 'append', then set mergeChildren == false...the recessive children
* will be appended as siblings of the dominant children.
*
* iii. otherwise, use the default value for mergeChildren, which is true...this is the same as
* specifying 'combine.children' == 'merge' as an attribute on the dominant root node.
*
* D. Iterate through the recessive children, and:
*
* i. if mergeChildren == true and there is a corresponding dominant child (matched by element name),
* merge the two.
*
* ii. otherwise, add the recessive child as a new child on the dominant root node.
*/
private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
{
// TODO: share this as some sort of assembler, implement a walk interface?
if ( recessive == null )
{
return;
}
boolean mergeSelf = true;
String selfMergeMode = dominant.getAttribute( SELF_COMBINATION_MODE_ATTRIBUTE );
if ( SELF_COMBINATION_OVERRIDE.equals( selfMergeMode ) )
{
mergeSelf = false;
}
if ( mergeSelf )
{
if ( isEmpty( dominant.getValue() ) )
{
dominant.setValue( recessive.getValue() );
}
String[] recessiveAttrs = recessive.getAttributeNames();
for ( int i = 0; i < recessiveAttrs.length; i++ )
{
String attr = recessiveAttrs[i];
if ( isEmpty( dominant.getAttribute( attr ) ) )
{
dominant.setAttribute( attr, recessive.getAttribute( attr ) );
}
}
if ( recessive.getChildCount() > 0 )
{
boolean mergeChildren = true;
if ( childMergeOverride != null )
{
mergeChildren = childMergeOverride.booleanValue();
}
else
{
String childMergeMode = dominant.getAttribute( CHILDREN_COMBINATION_MODE_ATTRIBUTE );
if ( CHILDREN_COMBINATION_APPEND.equals( childMergeMode ) )
{
mergeChildren = false;
}
}
if ( !mergeChildren )
{
Xpp3Dom[] dominantChildren = dominant.getChildren();
// remove these now, so we can append them to the recessive list later.
dominant.childList.clear();
for ( int i = 0, recessiveChildCount = recessive.getChildCount(); i < recessiveChildCount; i++ )
{
Xpp3Dom recessiveChild = recessive.getChild( i );
dominant.addChild( new Xpp3Dom( recessiveChild ) );
}
// now, re-add these children so they'll be appended to the recessive list.
for ( int i = 0; i < dominantChildren.length; i++ )
{
dominant.addChild( dominantChildren[i] );
}
}
else
{
Map commonChildren = new HashMap();
for ( Iterator it = recessive.childMap.keySet().iterator(); it.hasNext(); )
{
String childName = (String) it.next();
Xpp3Dom[] dominantChildren = dominant.getChildren( childName );
if ( dominantChildren.length > 0 )
{
commonChildren.put( childName, Arrays.asList( dominantChildren ).iterator() );
}
}
for ( int i = 0, recessiveChildCount = recessive.getChildCount(); i < recessiveChildCount; i++ )
{
Xpp3Dom recessiveChild = recessive.getChild( i );
Iterator it = (Iterator) commonChildren.get( recessiveChild.getName() );
if ( it == null )
{
dominant.addChild( new Xpp3Dom( recessiveChild ) );
}
else if ( it.hasNext() )
{
Xpp3Dom dominantChild = (Xpp3Dom) it.next();
mergeIntoXpp3Dom( dominantChild, recessiveChild, childMergeOverride );
}
}
}
}
}
}
/**
* Merge two DOMs, with one having dominance in the case of collision.
*
* @see #CHILDREN_COMBINATION_MODE_ATTRIBUTE
* @see #SELF_COMBINATION_MODE_ATTRIBUTE
*
* @param dominant The dominant DOM into which the recessive value/attributes/children will be merged
* @param recessive The recessive DOM, which will be merged into the dominant DOM
* @param childMergeOverride Overrides attribute flags to force merging or appending of child elements
* into the dominant DOM
*/
public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
{
if ( dominant != null )
{
mergeIntoXpp3Dom( dominant, recessive, childMergeOverride );
return dominant;
}
return recessive;
}
/**
* Merge two DOMs, with one having dominance in the case of collision.
* Merge mechanisms (vs. override for nodes, or vs. append for children) is determined by
* attributes of the dominant root node.
*
* @see #CHILDREN_COMBINATION_MODE_ATTRIBUTE
* @see #SELF_COMBINATION_MODE_ATTRIBUTE
*
* @param dominant The dominant DOM into which the recessive value/attributes/children will be merged
* @param recessive The recessive DOM, which will be merged into the dominant DOM
*/
public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive )
{
if ( dominant != null )
{
mergeIntoXpp3Dom( dominant, recessive, null );
return dominant;
}
return recessive;
}
// ----------------------------------------------------------------------
// Standard object handling
// ----------------------------------------------------------------------
public boolean equals( Object obj )
{
if ( obj == this )
{
return true;
}
if ( !( obj instanceof Xpp3Dom ) )
{
return false;
}
Xpp3Dom dom = (Xpp3Dom) obj;
if ( name == null ? dom.name != null : !name.equals( dom.name ) )
{
return false;
}
else if ( value == null ? dom.value != null : !value.equals( dom.value ) )
{
return false;
}
else if ( attributes == null ? dom.attributes != null : !attributes.equals( dom.attributes ) )
{
return false;
}
else if ( childList == null ? dom.childList != null : !childList.equals( dom.childList ) )
{
return false;
}
else
{
return true;
}
}
public int hashCode()
{
int result = 17;
result = 37 * result + ( name != null ? name.hashCode() : 0 );
result = 37 * result + ( value != null ? value.hashCode() : 0 );
result = 37 * result + ( attributes != null ? attributes.hashCode() : 0 );
result = 37 * result + ( childList != null ? childList.hashCode() : 0 );
return result;
}
public String toString()
{
// TODO: WARNING! Later versions of plexus-utils psit out an header due to thinking this is a new document - not the desired behaviour!
StringWriter writer = new StringWriter();
XMLWriter xmlWriter = new PrettyPrintXMLWriter( writer, "UTF-8", null );
Xpp3DomWriter.write( xmlWriter, this );
return writer.toString();
}
public String toUnescapedString()
{
// TODO: WARNING! Later versions of plexus-utils psit out an header due to thinking this is a new document - not the desired behaviour!
StringWriter writer = new StringWriter();
XMLWriter xmlWriter = new PrettyPrintXMLWriter( writer, "UTF-8", null );
Xpp3DomWriter.write( xmlWriter, this, false );
return writer.toString();
}
public static boolean isNotEmpty( String str )
{
return ( ( str != null ) && ( str.length() > 0 ) );
}
public static boolean isEmpty( String str )
{
return ( ( str == null ) || ( str.trim().length() == 0 ) );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomBuilder.java 0000664 0000000 0000000 00000012257 11660305702 0030044 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.xml;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.xml.pull.MXParser;
import org.codehaus.plexus.util.xml.pull.XmlPullParser;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
/**
* @version $Id: Xpp3DomBuilder.java 8010 2009-01-07 12:59:50Z vsiveton $
*/
public class Xpp3DomBuilder
{
private static final boolean DEFAULT_TRIM = true;
public static Xpp3Dom build( Reader reader )
throws XmlPullParserException, IOException
{
return build( reader, DEFAULT_TRIM );
}
public static Xpp3Dom build( InputStream is, String encoding )
throws XmlPullParserException, IOException
{
return build( is, encoding, DEFAULT_TRIM );
}
public static Xpp3Dom build( InputStream is, String encoding, boolean trim )
throws XmlPullParserException, IOException
{
XmlPullParser parser = new MXParser();
parser.setInput( is, encoding );
try
{
return build( parser, trim );
}
finally
{
IOUtil.close( is );
}
}
public static Xpp3Dom build( Reader reader, boolean trim )
throws XmlPullParserException, IOException
{
XmlPullParser parser = new MXParser();
parser.setInput( reader );
try
{
return build( parser, trim );
}
finally
{
IOUtil.close( reader );
}
}
public static Xpp3Dom build( XmlPullParser parser )
throws XmlPullParserException, IOException
{
return build( parser, DEFAULT_TRIM );
}
public static Xpp3Dom build( XmlPullParser parser, boolean trim )
throws XmlPullParserException, IOException
{
List elements = new ArrayList();
List values = new ArrayList();
int eventType = parser.getEventType();
while ( eventType != XmlPullParser.END_DOCUMENT )
{
if ( eventType == XmlPullParser.START_TAG )
{
String rawName = parser.getName();
Xpp3Dom childConfiguration = new Xpp3Dom( rawName );
int depth = elements.size();
if ( depth > 0 )
{
Xpp3Dom parent = (Xpp3Dom) elements.get( depth - 1 );
parent.addChild( childConfiguration );
}
elements.add( childConfiguration );
if ( parser.isEmptyElementTag() )
{
values.add( null );
}
else
{
values.add( new StringBuffer() );
}
int attributesSize = parser.getAttributeCount();
for ( int i = 0; i < attributesSize; i++ )
{
String name = parser.getAttributeName( i );
String value = parser.getAttributeValue( i );
childConfiguration.setAttribute( name, value );
}
}
else if ( eventType == XmlPullParser.TEXT )
{
int depth = values.size() - 1;
StringBuffer valueBuffer = (StringBuffer) values.get( depth );
String text = parser.getText();
if ( trim )
{
text = text.trim();
}
valueBuffer.append( text );
}
else if ( eventType == XmlPullParser.END_TAG )
{
int depth = elements.size() - 1;
Xpp3Dom finishedConfiguration = (Xpp3Dom) elements.remove( depth );
/* this Object could be null if it is a singleton tag */
Object accumulatedValue = values.remove( depth );
if ( finishedConfiguration.getChildCount() == 0 )
{
if ( accumulatedValue == null )
{
finishedConfiguration.setValue( null );
}
else
{
finishedConfiguration.setValue( accumulatedValue.toString() );
}
}
if ( depth == 0 )
{
return finishedConfiguration;
}
}
eventType = parser.next();
}
throw new IllegalStateException( "End of document found before returning to 0 depth" );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java 0000664 0000000 0000000 00000021405 11660305702 0027551 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.xml;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import org.codehaus.plexus.util.xml.pull.XmlSerializer;
import java.io.IOException;
/** @author Jason van Zyl */
public class Xpp3DomUtils
{
public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children";
public static final String CHILDREN_COMBINATION_MERGE = "merge";
public static final String CHILDREN_COMBINATION_APPEND = "append";
/**
* This default mode for combining children DOMs during merge means that where element names
* match, the process will try to merge the element data, rather than putting the dominant
* and recessive elements (which share the same element name) as siblings in the resulting
* DOM.
*/
public static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE;
public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self";
public static final String SELF_COMBINATION_OVERRIDE = "override";
public static final String SELF_COMBINATION_MERGE = "merge";
/**
* This default mode for combining a DOM node during merge means that where element names
* match, the process will try to merge the element attributes and values, rather than
* overriding the recessive element completely with the dominant one. This means that
* wherever the dominant element doesn't provide the value or a particular attribute, that
* value or attribute will be set from the recessive DOM node.
*/
public static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE;
public void writeToSerializer( String namespace, XmlSerializer serializer, Xpp3Dom dom )
throws IOException
{
// TODO: WARNING! Later versions of plexus-utils psit out an header due to thinking this is a new document - not the desired behaviour!
SerializerXMLWriter xmlWriter = new SerializerXMLWriter( namespace, serializer );
Xpp3DomWriter.write( xmlWriter, dom );
if ( xmlWriter.getExceptions().size() > 0 )
{
throw (IOException) xmlWriter.getExceptions().get( 0 );
}
}
/**
* Merges one DOM into another, given a specific algorithm and possible override points for that algorithm.
* The algorithm is as follows:
*
* 1. if the recessive DOM is null, there is nothing to do...return.
*
* 2. Determine whether the dominant node will suppress the recessive one (flag=mergeSelf).
*
* A. retrieve the 'combine.self' attribute on the dominant node, and try to match against 'override'...
* if it matches 'override', then set mergeSelf == false...the dominant node suppresses the recessive
* one completely.
*
* B. otherwise, use the default value for mergeSelf, which is true...this is the same as specifying
* 'combine.self' == 'merge' as an attribute of the dominant root node.
*
* 3. If mergeSelf == true
*
* A. if the dominant root node's value is empty, set it to the recessive root node's value
*
* B. For each attribute in the recessive root node which is not set in the dominant root node, set it.
*
* C. Determine whether children from the recessive DOM will be merged or appended to the dominant
* DOM as siblings (flag=mergeChildren).
*
* i. if childMergeOverride is set (non-null), use that value (true/false)
*
* ii. retrieve the 'combine.children' attribute on the dominant node, and try to match against
* 'append'...if it matches 'append', then set mergeChildren == false...the recessive children
* will be appended as siblings of the dominant children.
*
* iii. otherwise, use the default value for mergeChildren, which is true...this is the same as
* specifying 'combine.children' == 'merge' as an attribute on the dominant root node.
*
* D. Iterate through the recessive children, and:
*
* i. if mergeChildren == true and there is a corresponding dominant child (matched by element name),
* merge the two.
*
* ii. otherwise, add the recessive child as a new child on the dominant root node.
*/
private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
{
// TODO: share this as some sort of assembler, implement a walk interface?
if ( recessive == null )
{
return;
}
boolean mergeSelf = true;
String selfMergeMode = dominant.getAttribute( SELF_COMBINATION_MODE_ATTRIBUTE );
if ( isNotEmpty( selfMergeMode ) && SELF_COMBINATION_OVERRIDE.equals( selfMergeMode ) )
{
mergeSelf = false;
}
if ( mergeSelf )
{
if ( isEmpty( dominant.getValue() ) )
{
dominant.setValue( recessive.getValue() );
}
String[] recessiveAttrs = recessive.getAttributeNames();
for ( int i = 0; i < recessiveAttrs.length; i++ )
{
String attr = recessiveAttrs[i];
if ( isEmpty( dominant.getAttribute( attr ) ) )
{
dominant.setAttribute( attr, recessive.getAttribute( attr ) );
}
}
boolean mergeChildren = true;
if ( childMergeOverride != null )
{
mergeChildren = childMergeOverride.booleanValue();
}
else
{
String childMergeMode = dominant.getAttribute( CHILDREN_COMBINATION_MODE_ATTRIBUTE );
if ( isNotEmpty( childMergeMode ) && CHILDREN_COMBINATION_APPEND.equals( childMergeMode ) )
{
mergeChildren = false;
}
}
Xpp3Dom[] children = recessive.getChildren();
for ( int i = 0; i < children.length; i++ )
{
Xpp3Dom child = children[i];
Xpp3Dom childDom = dominant.getChild( child.getName() );
if ( mergeChildren && childDom != null )
{
mergeIntoXpp3Dom( childDom, child, childMergeOverride );
}
else
{
dominant.addChild( new Xpp3Dom( child ) );
}
}
}
}
/**
* Merge two DOMs, with one having dominance in the case of collision.
*
* @see #CHILDREN_COMBINATION_MODE_ATTRIBUTE
* @see #SELF_COMBINATION_MODE_ATTRIBUTE
*
* @param dominant The dominant DOM into which the recessive value/attributes/children will be merged
* @param recessive The recessive DOM, which will be merged into the dominant DOM
* @param childMergeOverride Overrides attribute flags to force merging or appending of child elements
* into the dominant DOM
*/
public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
{
if ( dominant != null )
{
mergeIntoXpp3Dom( dominant, recessive, childMergeOverride );
return dominant;
}
return recessive;
}
/**
* Merge two DOMs, with one having dominance in the case of collision.
* Merge mechanisms (vs. override for nodes, or vs. append for children) is determined by
* attributes of the dominant root node.
*
* @see #CHILDREN_COMBINATION_MODE_ATTRIBUTE
* @see #SELF_COMBINATION_MODE_ATTRIBUTE
*
* @param dominant The dominant DOM into which the recessive value/attributes/children will be merged
* @param recessive The recessive DOM, which will be merged into the dominant DOM
*/
public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive )
{
if ( dominant != null )
{
mergeIntoXpp3Dom( dominant, recessive, null );
return dominant;
}
return recessive;
}
public static boolean isNotEmpty( String str )
{
return ( str != null && str.length() > 0 );
}
public static boolean isEmpty( String str )
{
return ( str == null || str.trim().length() == 0 );
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomWriter.java 0000664 0000000 0000000 00000004130 11660305702 0027721 0 ustar 00root root 0000000 0000000 package org.codehaus.plexus.util.xml;
/*
* Copyright The Codehaus Foundation.
*
* 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.
*/
import java.io.PrintWriter;
import java.io.Writer;
/**
* @version $Id: Xpp3DomWriter.java 8660 2010-03-05 21:50:36Z hboutemy $
*/
public class Xpp3DomWriter
{
public static void write( Writer writer, Xpp3Dom dom )
{
write( new PrettyPrintXMLWriter( writer ), dom );
}
public static void write( PrintWriter writer, Xpp3Dom dom )
{
write( new PrettyPrintXMLWriter( writer ), dom );
}
public static void write( XMLWriter xmlWriter, Xpp3Dom dom )
{
write( xmlWriter, dom, true );
}
public static void write( XMLWriter xmlWriter, Xpp3Dom dom, boolean escape )
{
// TODO: move to XMLWriter?
xmlWriter.startElement( dom.getName() );
String[] attributeNames = dom.getAttributeNames();
for ( int i = 0; i < attributeNames.length; i++ )
{
String attributeName = attributeNames[i];
xmlWriter.addAttribute( attributeName, dom.getAttribute( attributeName ) );
}
Xpp3Dom[] children = dom.getChildren();
for ( int i = 0; i < children.length; i++ )
{
write( xmlWriter, children[i], escape );
}
String value = dom.getValue();
if ( value != null )
{
if ( escape )
{
xmlWriter.writeText( value );
}
else
{
xmlWriter.writeMarkup( value );
}
}
xmlWriter.endElement();
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/pull/ 0000775 0000000 0000000 00000000000 11660305702 0025345 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/pull/MXParser.java 0000664 0000000 0000000 00000410731 11660305702 0027717 0 ustar 00root root 0000000 0000000 /* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/
/*
* Copyright (c) 2003 Extreme! Lab, Indiana University. All rights reserved.
*
* This software is open source. See the bottom of this file for the licence.
*
* $Id: MXParser.java 8238 2009-05-28 23:51:31Z vsiveton $
*/
package org.codehaus.plexus.util.xml.pull;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import org.codehaus.plexus.util.ReaderFactory;
//import java.util.Hashtable;
//TODO best handling of interning issues
// have isAllNewStringInterned ???
//TODO handling surrogate pairs: http://www.unicode.org/unicode/faq/utf_bom.html#6
//TODO review code for use of bufAbsoluteStart when keeping pos between next()/fillBuf()
/**
* Absolutely minimal implementation of XMLPULL V1 API. Encoding handling done with XmlReader
*
* @see org.codehaus.plexus.util.xml.XmlReader
* @author Aleksander Slominski
*/
public class MXParser
implements XmlPullParser
{
//NOTE: no interning of those strings --> by Java leng spec they MUST be already interned
protected final static String XML_URI = "http://www.w3.org/XML/1998/namespace";
protected final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
protected final static String FEATURE_XML_ROUNDTRIP=
//"http://xmlpull.org/v1/doc/features.html#xml-roundtrip";
"http://xmlpull.org/v1/doc/features.html#xml-roundtrip";
protected final static String FEATURE_NAMES_INTERNED =
"http://xmlpull.org/v1/doc/features.html#names-interned";
protected final static String PROPERTY_XMLDECL_VERSION =
"http://xmlpull.org/v1/doc/properties.html#xmldecl-version";
protected final static String PROPERTY_XMLDECL_STANDALONE =
"http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone";
protected final static String PROPERTY_XMLDECL_CONTENT =
"http://xmlpull.org/v1/doc/properties.html#xmldecl-content";
protected final static String PROPERTY_LOCATION =
"http://xmlpull.org/v1/doc/properties.html#location";
/**
* Implementation notice:
* the is instance variable that controls if newString() is interning.
* NOTE: newStringIntern always returns interned strings
* and newString MAY return interned String depending on this variable.
*
NOTE: by default in this minimal implementation it is false!
*/
protected boolean allStringsInterned;
protected void resetStringCache() {
//System.out.println("resetStringCache() minimum called");
}
protected String newString(char[] cbuf, int off, int len) {
return new String(cbuf, off, len);
}
protected String newStringIntern(char[] cbuf, int off, int len) {
return (new String(cbuf, off, len)).intern();
}
private static final boolean TRACE_SIZING = false;
// NOTE: features are not resetable and typically defaults to false ...
protected boolean processNamespaces;
protected boolean roundtripSupported;
// global parser state
protected String location;
protected int lineNumber;
protected int columnNumber;
protected boolean seenRoot;
protected boolean reachedEnd;
protected int eventType;
protected boolean emptyElementTag;
// element stack
protected int depth;
protected char[] elRawName[];
protected int elRawNameEnd[];
protected int elRawNameLine[];
protected String elName[];
protected String elPrefix[];
protected String elUri[];
//protected String elValue[];
protected int elNamespaceCount[];
/**
* Make sure that we have enough space to keep element stack if passed size.
* It will always create one additional slot then current depth
*/
protected void ensureElementsCapacity() {
final int elStackSize = elName != null ? elName.length : 0;
if( (depth + 1) >= elStackSize) {
// we add at least one extra slot ...
final int newSize = (depth >= 7 ? 2 * depth : 8) + 2; // = lucky 7 + 1 //25
if(TRACE_SIZING) {
System.err.println("TRACE_SIZING elStackSize "+elStackSize+" ==> "+newSize);
}
final boolean needsCopying = elStackSize > 0;
String[] arr = null;
// resue arr local variable slot
arr = new String[newSize];
if(needsCopying) System.arraycopy(elName, 0, arr, 0, elStackSize);
elName = arr;
arr = new String[newSize];
if(needsCopying) System.arraycopy(elPrefix, 0, arr, 0, elStackSize);
elPrefix = arr;
arr = new String[newSize];
if(needsCopying) System.arraycopy(elUri, 0, arr, 0, elStackSize);
elUri = arr;
int[] iarr = new int[newSize];
if(needsCopying) {
System.arraycopy(elNamespaceCount, 0, iarr, 0, elStackSize);
} else {
// special initialization
iarr[0] = 0;
}
elNamespaceCount = iarr;
//TODO: avoid using element raw name ...
iarr = new int[newSize];
if(needsCopying) {
System.arraycopy(elRawNameEnd, 0, iarr, 0, elStackSize);
}
elRawNameEnd = iarr;
iarr = new int[newSize];
if(needsCopying) {
System.arraycopy(elRawNameLine, 0, iarr, 0, elStackSize);
}
elRawNameLine = iarr;
final char[][] carr = new char[newSize][];
if(needsCopying) {
System.arraycopy(elRawName, 0, carr, 0, elStackSize);
}
elRawName = carr;
// arr = new String[newSize];
// if(needsCopying) System.arraycopy(elLocalName, 0, arr, 0, elStackSize);
// elLocalName = arr;
// arr = new String[newSize];
// if(needsCopying) System.arraycopy(elDefaultNs, 0, arr, 0, elStackSize);
// elDefaultNs = arr;
// int[] iarr = new int[newSize];
// if(needsCopying) System.arraycopy(elNsStackPos, 0, iarr, 0, elStackSize);
// for (int i = elStackSize; i < iarr.length; i++)
// {
// iarr[i] = (i > 0) ? -1 : 0;
// }
// elNsStackPos = iarr;
//assert depth < elName.length;
}
}
// attribute stack
protected int attributeCount;
protected String attributeName[];
protected int attributeNameHash[];
//protected int attributeNameStart[];
//protected int attributeNameEnd[];
protected String attributePrefix[];
protected String attributeUri[];
protected String attributeValue[];
//protected int attributeValueStart[];
//protected int attributeValueEnd[];
/**
* Make sure that in attributes temporary array is enough space.
*/
protected void ensureAttributesCapacity(int size) {
final int attrPosSize = attributeName != null ? attributeName.length : 0;
if(size >= attrPosSize) {
final int newSize = size > 7 ? 2 * size : 8; // = lucky 7 + 1 //25
if(TRACE_SIZING) {
System.err.println("TRACE_SIZING attrPosSize "+attrPosSize+" ==> "+newSize);
}
final boolean needsCopying = attrPosSize > 0;
String[] arr = null;
arr = new String[newSize];
if(needsCopying) System.arraycopy(attributeName, 0, arr, 0, attrPosSize);
attributeName = arr;
arr = new String[newSize];
if(needsCopying) System.arraycopy(attributePrefix, 0, arr, 0, attrPosSize);
attributePrefix = arr;
arr = new String[newSize];
if(needsCopying) System.arraycopy(attributeUri, 0, arr, 0, attrPosSize);
attributeUri = arr;
arr = new String[newSize];
if(needsCopying) System.arraycopy(attributeValue, 0, arr, 0, attrPosSize);
attributeValue = arr;
if( ! allStringsInterned ) {
final int[] iarr = new int[newSize];
if(needsCopying) System.arraycopy(attributeNameHash, 0, iarr, 0, attrPosSize);
attributeNameHash = iarr;
}
arr = null;
// //assert attrUri.length > size
}
}
// namespace stack
protected int namespaceEnd;
protected String namespacePrefix[];
protected int namespacePrefixHash[];
protected String namespaceUri[];
protected void ensureNamespacesCapacity(int size) {
final int namespaceSize = namespacePrefix != null ? namespacePrefix.length : 0;
if(size >= namespaceSize) {
final int newSize = size > 7 ? 2 * size : 8; // = lucky 7 + 1 //25
if(TRACE_SIZING) {
System.err.println("TRACE_SIZING namespaceSize "+namespaceSize+" ==> "+newSize);
}
final String[] newNamespacePrefix = new String[newSize];
final String[] newNamespaceUri = new String[newSize];
if(namespacePrefix != null) {
System.arraycopy(
namespacePrefix, 0, newNamespacePrefix, 0, namespaceEnd);
System.arraycopy(
namespaceUri, 0, newNamespaceUri, 0, namespaceEnd);
}
namespacePrefix = newNamespacePrefix;
namespaceUri = newNamespaceUri;
if( ! allStringsInterned ) {
final int[] newNamespacePrefixHash = new int[newSize];
if(namespacePrefixHash != null) {
System.arraycopy(
namespacePrefixHash, 0, newNamespacePrefixHash, 0, namespaceEnd);
}
namespacePrefixHash = newNamespacePrefixHash;
}
//prefixesSize = newSize;
// //assert nsPrefixes.length > size && nsPrefixes.length == newSize
}
}
/**
* simplistic implementation of hash function that has constant
* time to compute - so it also means diminishing hash quality for long strings
* but for XML parsing it should be good enough ...
*/
protected static final int fastHash( char ch[], int off, int len ) {
if(len == 0) return 0;
//assert len >0
int hash = ch[off]; // hash at beginning
//try {
hash = (hash << 7) + ch[ off + len - 1 ]; // hash at the end
//} catch(ArrayIndexOutOfBoundsException aie) {
// aie.printStackTrace(); //should never happen ...
// throw new RuntimeException("this is violation of pre-condition");
//}
if(len > 16) hash = (hash << 7) + ch[ off + (len / 4)]; // 1/4 from beginning
if(len > 8) hash = (hash << 7) + ch[ off + (len / 2)]; // 1/2 of string size ...
// notice that hash is at most done 3 times <<7 so shifted by 21 bits 8 bit value
// so max result == 29 bits so it is quite just below 31 bits for long (2^32) ...
//assert hash >= 0;
return hash;
}
// entity replacement stack
protected int entityEnd;
protected String entityName[];
protected char[] entityNameBuf[];
protected String entityReplacement[];
protected char[] entityReplacementBuf[];
protected int entityNameHash[];
protected void ensureEntityCapacity() {
final int entitySize = entityReplacementBuf != null ? entityReplacementBuf.length : 0;
if(entityEnd >= entitySize) {
final int newSize = entityEnd > 7 ? 2 * entityEnd : 8; // = lucky 7 + 1 //25
if(TRACE_SIZING) {
System.err.println("TRACE_SIZING entitySize "+entitySize+" ==> "+newSize);
}
final String[] newEntityName = new String[newSize];
final char[] newEntityNameBuf[] = new char[newSize][];
final String[] newEntityReplacement = new String[newSize];
final char[] newEntityReplacementBuf[] = new char[newSize][];
if(entityName != null) {
System.arraycopy(entityName, 0, newEntityName, 0, entityEnd);
System.arraycopy(entityNameBuf, 0, newEntityNameBuf, 0, entityEnd);
System.arraycopy(entityReplacement, 0, newEntityReplacement, 0, entityEnd);
System.arraycopy(entityReplacementBuf, 0, newEntityReplacementBuf, 0, entityEnd);
}
entityName = newEntityName;
entityNameBuf = newEntityNameBuf;
entityReplacement = newEntityReplacement;
entityReplacementBuf = newEntityReplacementBuf;
if( ! allStringsInterned ) {
final int[] newEntityNameHash = new int[newSize];
if(entityNameHash != null) {
System.arraycopy(entityNameHash, 0, newEntityNameHash, 0, entityEnd);
}
entityNameHash = newEntityNameHash;
}
}
}
// input buffer management
protected static final int READ_CHUNK_SIZE = 8*1024; //max data chars in one read() call
protected Reader reader;
protected String inputEncoding;
protected int bufLoadFactor = 95; // 99%
//protected int bufHardLimit; // only matters when expanding
protected char buf[] = new char[
Runtime.getRuntime().freeMemory() > 1000000L ? READ_CHUNK_SIZE : 256 ];
protected int bufSoftLimit = ( bufLoadFactor * buf.length ) /100; // desirable size of buffer
protected boolean preventBufferCompaction;
protected int bufAbsoluteStart; // this is buf
protected int bufStart;
protected int bufEnd;
protected int pos;
protected int posStart;
protected int posEnd;
protected char pc[] = new char[
Runtime.getRuntime().freeMemory() > 1000000L ? READ_CHUNK_SIZE : 64 ];
protected int pcStart;
protected int pcEnd;
// parsing state
//protected boolean needsMore;
//protected boolean seenMarkup;
protected boolean usePC;
protected boolean seenStartTag;
protected boolean seenEndTag;
protected boolean pastEndTag;
protected boolean seenAmpersand;
protected boolean seenMarkup;
protected boolean seenDocdecl;
// transient variable set during each call to next/Token()
protected boolean tokenize;
protected String text;
protected String entityRefName;
protected String xmlDeclVersion;
protected Boolean xmlDeclStandalone;
protected String xmlDeclContent;
protected void reset() {
//System.out.println("reset() called");
location = null;
lineNumber = 1;
columnNumber = 0;
seenRoot = false;
reachedEnd = false;
eventType = START_DOCUMENT;
emptyElementTag = false;
depth = 0;
attributeCount = 0;
namespaceEnd = 0;
entityEnd = 0;
reader = null;
inputEncoding = null;
preventBufferCompaction = false;
bufAbsoluteStart = 0;
bufEnd = bufStart = 0;
pos = posStart = posEnd = 0;
pcEnd = pcStart = 0;
usePC = false;
seenStartTag = false;
seenEndTag = false;
pastEndTag = false;
seenAmpersand = false;
seenMarkup = false;
seenDocdecl = false;
xmlDeclVersion = null;
xmlDeclStandalone = null;
xmlDeclContent = null;
resetStringCache();
}
public MXParser() {
}
/**
* Method setFeature
*
* @param name a String
* @param state a boolean
*
* @throws XmlPullParserException
*
*/
public void setFeature(String name,
boolean state) throws XmlPullParserException
{
if(name == null) throw new IllegalArgumentException("feature name should not be null");
if(FEATURE_PROCESS_NAMESPACES.equals(name)) {
if(eventType != START_DOCUMENT) throw new XmlPullParserException(
"namespace processing feature can only be changed before parsing", this, null);
processNamespaces = state;
// } else if(FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
// if(type != START_DOCUMENT) throw new XmlPullParserException(
// "namespace reporting feature can only be changed before parsing", this, null);
// reportNsAttribs = state;
} else if(FEATURE_NAMES_INTERNED.equals(name)) {
if(state != false) {
throw new XmlPullParserException(
"interning names in this implementation is not supported");
}
} else if(FEATURE_PROCESS_DOCDECL.equals(name)) {
if(state != false) {
throw new XmlPullParserException(
"processing DOCDECL is not supported");
}
//} else if(REPORT_DOCDECL.equals(name)) {
// paramNotifyDoctype = state;
} else if(FEATURE_XML_ROUNDTRIP.equals(name)) {
//if(state == false) {
// throw new XmlPullParserException(
// "roundtrip feature can not be switched off");
//}
roundtripSupported = state;
} else {
throw new XmlPullParserException("unsupporte feature "+name);
}
}
/** Unknown properties are always returned as false */
public boolean getFeature(String name)
{
if(name == null) throw new IllegalArgumentException("feature name should not be nulll");
if(FEATURE_PROCESS_NAMESPACES.equals(name)) {
return processNamespaces;
// } else if(FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
// return reportNsAttribs;
} else if(FEATURE_NAMES_INTERNED.equals(name)) {
return false;
} else if(FEATURE_PROCESS_DOCDECL.equals(name)) {
return false;
//} else if(REPORT_DOCDECL.equals(name)) {
// return paramNotifyDoctype;
} else if(FEATURE_XML_ROUNDTRIP.equals(name)) {
//return true;
return roundtripSupported;
}
return false;
}
public void setProperty(String name,
Object value)
throws XmlPullParserException
{
if(PROPERTY_LOCATION.equals(name)) {
location = (String) value;
} else {
throw new XmlPullParserException("unsupported property: '"+name+"'");
}
}
public Object getProperty(String name)
{
if(name == null) throw new IllegalArgumentException("property name should not be nulll");
if(PROPERTY_XMLDECL_VERSION.equals(name)) {
return xmlDeclVersion;
} else if(PROPERTY_XMLDECL_STANDALONE.equals(name)) {
return xmlDeclStandalone;
} else if(PROPERTY_XMLDECL_CONTENT.equals(name)) {
return xmlDeclContent;
} else if(PROPERTY_LOCATION.equals(name)) {
return location;
}
return null;
}
public void setInput(Reader in) throws XmlPullParserException
{
reset();
reader = in;
}
public void setInput(java.io.InputStream inputStream, String inputEncoding)
throws XmlPullParserException
{
if(inputStream == null) {
throw new IllegalArgumentException("input stream can not be null");
}
Reader reader;
try {
if(inputEncoding != null) {
reader = ReaderFactory.newReader(inputStream, inputEncoding);
} else {
reader = ReaderFactory.newXmlReader(inputStream);
}
} catch (UnsupportedEncodingException une) {
throw new XmlPullParserException(
"could not create reader for encoding "+inputEncoding+" : "+une, this, une);
}
catch ( IOException e )
{
throw new XmlPullParserException(
"could not create reader : "+e, this, e);
}
setInput(reader);
//must be here as reset() was called in setInput() and has set this.inputEncoding to null ...
this.inputEncoding = inputEncoding;
}
public String getInputEncoding() {
return inputEncoding;
}
public void defineEntityReplacementText(String entityName,
String replacementText)
throws XmlPullParserException
{
// throw new XmlPullParserException("not allowed");
if ( !replacementText.startsWith( "" ) && this.entityName != null && replacementText.length() > 1 )
{
String tmp = replacementText.substring( 1, replacementText.length() - 1 );
for ( int i = 0; i < this.entityName.length; i++ )
{
if ( this.entityName[i] != null && this.entityName[i].equals( tmp ) )
{
replacementText = this.entityReplacement[i];
}
}
}
//protected char[] entityReplacement[];
ensureEntityCapacity();
// this is to make sure that if interning works we will take advantage of it ...
this.entityName[entityEnd] = newString(entityName.toCharArray(), 0, entityName.length());
entityNameBuf[entityEnd] = entityName.toCharArray();
entityReplacement[entityEnd] = replacementText;
entityReplacementBuf[entityEnd] = replacementText.toCharArray();
if(!allStringsInterned) {
entityNameHash[ entityEnd ] =
fastHash(entityNameBuf[entityEnd], 0, entityNameBuf[entityEnd].length);
}
++entityEnd;
//TODO disallow < or & in entity replacement text (or ]]>???)
// TOOD keepEntityNormalizedForAttributeValue cached as well ...
}
public int getNamespaceCount(int depth)
throws XmlPullParserException
{
if(processNamespaces == false || depth == 0) {
return 0;
}
//int maxDepth = eventType == END_TAG ? this.depth + 1 : this.depth;
//if(depth < 0 || depth > maxDepth) throw new IllegalArgumentException(
if(depth < 0 || depth > this.depth) throw new IllegalArgumentException(
"napespace count mayt be for depth 0.."+this.depth+" not "+depth);
return elNamespaceCount[ depth ];
}
public String getNamespacePrefix(int pos)
throws XmlPullParserException
{
//int end = eventType == END_TAG ? elNamespaceCount[ depth + 1 ] : namespaceEnd;
//if(pos < end) {
if(pos < namespaceEnd) {
return namespacePrefix[ pos ];
} else {
throw new XmlPullParserException(
"position "+pos+" exceeded number of available namespaces "+namespaceEnd);
}
}
public String getNamespaceUri(int pos) throws XmlPullParserException
{
//int end = eventType == END_TAG ? elNamespaceCount[ depth + 1 ] : namespaceEnd;
//if(pos < end) {
if(pos < namespaceEnd) {
return namespaceUri[ pos ];
} else {
throw new XmlPullParserException(
"position "+pos+" exceedded number of available namespaces "+namespaceEnd);
}
}
public String getNamespace( String prefix )
//throws XmlPullParserException
{
//int count = namespaceCount[ depth ];
if(prefix != null) {
for( int i = namespaceEnd -1; i >= 0; i--) {
if( prefix.equals( namespacePrefix[ i ] ) ) {
return namespaceUri[ i ];
}
}
if("xml".equals( prefix )) {
return XML_URI;
} else if("xmlns".equals( prefix )) {
return XMLNS_URI;
}
} else {
for( int i = namespaceEnd -1; i >= 0; i--) {
if( namespacePrefix[ i ] == null) { //"") { //null ) { //TODO check FIXME Alek
return namespaceUri[ i ];
}
}
}
return null;
}
public int getDepth()
{
return depth;
}
private static int findFragment(int bufMinPos, char[] b, int start, int end) {
//System.err.println("bufStart="+bufStart+" b="+printable(new String(b, start, end - start))+" start="+start+" end="+end);
if(start < bufMinPos) {
start = bufMinPos;
if(start > end) start = end;
return start;
}
if(end - start > 65) {
start = end - 10; // try to find good location
}
int i = start + 1;
while(--i > bufMinPos) {
if((end - i) > 65) break;
final char c = b[i];
if(c == '<' && (start - i) > 10) break;
}
return i;
}
/**
* Return string describing current position of parsers as
* text 'STATE [seen %s...] @line:column'.
*/
public String getPositionDescription ()
{
String fragment = null;
if(posStart <= pos) {
final int start = findFragment(0, buf, posStart, pos);
//System.err.println("start="+start);
if(start < pos) {
fragment = new String(buf, start, pos - start);
}
if(bufAbsoluteStart > 0 || start > 0) fragment = "..." + fragment;
}
// return " at line "+tokenizerPosRow
// +" and column "+(tokenizerPosCol-1)
// +(fragment != null ? " seen "+printable(fragment)+"..." : "");
return " "+TYPES[ eventType ] +
(fragment != null ? " seen "+printable(fragment)+"..." : "")
+" "+(location != null ? location : "")
+"@"+getLineNumber()+":"+getColumnNumber();
}
public int getLineNumber()
{
return lineNumber;
}
public int getColumnNumber()
{
return columnNumber;
}
public boolean isWhitespace() throws XmlPullParserException
{
if(eventType == TEXT || eventType == CDSECT) {
if(usePC) {
for (int i = pcStart; i = 0; i--) {
// if( prefix.equals( namespacePrefix[ i ] ) ) {
// return namespaceUri[ i ];
// }
// }
// } else {
// for( int i = namespaceEnd -1; i >= 0; i--) {
// if( namespacePrefix[ i ] == null ) {
// return namespaceUri[ i ];
// }
// }
//
// }
// return "";
}
public String getName()
{
if(eventType == START_TAG) {
//return elName[ depth - 1 ] ;
return elName[ depth ] ;
} else if(eventType == END_TAG) {
return elName[ depth ] ;
} else if(eventType == ENTITY_REF) {
if(entityRefName == null) {
entityRefName = newString(buf, posStart, posEnd - posStart);
}
return entityRefName;
} else {
return null;
}
}
public String getPrefix()
{
if(eventType == START_TAG) {
//return elPrefix[ depth - 1 ] ;
return elPrefix[ depth ] ;
} else if(eventType == END_TAG) {
return elPrefix[ depth ] ;
}
return null;
// if(eventType != START_TAG && eventType != END_TAG) return null;
// int maxDepth = eventType == END_TAG ? depth : depth - 1;
// return elPrefix[ maxDepth ];
}
public boolean isEmptyElementTag() throws XmlPullParserException
{
if(eventType != START_TAG) throw new XmlPullParserException(
"parser must be on START_TAG to check for empty element", this, null);
return emptyElementTag;
}
public int getAttributeCount()
{
if(eventType != START_TAG) return -1;
return attributeCount;
}
public String getAttributeNamespace(int index)
{
if(eventType != START_TAG) throw new IndexOutOfBoundsException(
"only START_TAG can have attributes");
if(processNamespaces == false) return NO_NAMESPACE;
if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
"attribute position must be 0.."+(attributeCount-1)+" and not "+index);
return attributeUri[ index ];
}
public String getAttributeName(int index)
{
if(eventType != START_TAG) throw new IndexOutOfBoundsException(
"only START_TAG can have attributes");
if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
"attribute position must be 0.."+(attributeCount-1)+" and not "+index);
return attributeName[ index ];
}
public String getAttributePrefix(int index)
{
if(eventType != START_TAG) throw new IndexOutOfBoundsException(
"only START_TAG can have attributes");
if(processNamespaces == false) return null;
if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
"attribute position must be 0.."+(attributeCount-1)+" and not "+index);
return attributePrefix[ index ];
}
public String getAttributeType(int index) {
if(eventType != START_TAG) throw new IndexOutOfBoundsException(
"only START_TAG can have attributes");
if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
"attribute position must be 0.."+(attributeCount-1)+" and not "+index);
return "CDATA";
}
public boolean isAttributeDefault(int index) {
if(eventType != START_TAG) throw new IndexOutOfBoundsException(
"only START_TAG can have attributes");
if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
"attribute position must be 0.."+(attributeCount-1)+" and not "+index);
return false;
}
public String getAttributeValue(int index)
{
if(eventType != START_TAG) throw new IndexOutOfBoundsException(
"only START_TAG can have attributes");
if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException(
"attribute position must be 0.."+(attributeCount-1)+" and not "+index);
return attributeValue[ index ];
}
public String getAttributeValue(String namespace,
String name)
{
if(eventType != START_TAG) throw new IndexOutOfBoundsException(
"only START_TAG can have attributes"+getPositionDescription());
if(name == null) {
throw new IllegalArgumentException("attribute name can not be null");
}
// TODO make check if namespace is interned!!! etc. for names!!!
if(processNamespaces) {
if(namespace == null) {
namespace = "";
}
for(int i = 0; i < attributeCount; ++i) {
if((namespace == attributeUri[ i ] ||
namespace.equals(attributeUri[ i ]) )
//(namespace != null && namespace.equals(attributeUri[ i ]))
// taking advantage of String.intern()
&& name.equals(attributeName[ i ]) )
{
return attributeValue[i];
}
}
} else {
if(namespace != null && namespace.length() == 0) {
namespace = null;
}
if(namespace != null) throw new IllegalArgumentException(
"when namespaces processing is disabled attribute namespace must be null");
for(int i = 0; i < attributeCount; ++i) {
if(name.equals(attributeName[i]))
{
return attributeValue[i];
}
}
}
return null;
}
public int getEventType()
throws XmlPullParserException
{
return eventType;
}
public void require(int type, String namespace, String name)
throws XmlPullParserException, IOException
{
if(processNamespaces == false && namespace != null) {
throw new XmlPullParserException(
"processing namespaces must be enabled on parser (or factory)"+
" to have possible namespaces delcared on elements"
+(" (postion:"+ getPositionDescription())+")");
}
if (type != getEventType()
|| (namespace != null && !namespace.equals (getNamespace()))
|| (name != null && !name.equals (getName ())) )
{
throw new XmlPullParserException (
"expected event "+TYPES[ type ]
+(name != null ? " with name '"+name+"'" : "")
+(namespace != null && name != null ? " and" : "")
+(namespace != null ? " with namespace '"+namespace+"'" : "")
+" but got"
+(type != getEventType() ? " "+TYPES[ getEventType() ] : "")
+(name != null && getName() != null && !name.equals (getName ())
? " name '"+getName()+"'" : "")
+(namespace != null && name != null
&& getName() != null && !name.equals (getName ())
&& getNamespace() != null && !namespace.equals (getNamespace())
? " and" : "")
+(namespace != null && getNamespace() != null && !namespace.equals (getNamespace())
? " namespace '"+getNamespace()+"'" : "")
+(" (postion:"+ getPositionDescription())+")");
}
}
/**
* Skip sub tree that is currently porser positioned on.
*
NOTE: parser must be on START_TAG and when funtion returns
* parser will be positioned on corresponding END_TAG
*/
public void skipSubTree()
throws XmlPullParserException, IOException
{
require(START_TAG, null, null);
int level = 1;
while(level > 0) {
int eventType = next();
if(eventType == END_TAG) {
--level;
} else if(eventType == START_TAG) {
++level;
}
}
}
// public String readText() throws XmlPullParserException, IOException
// {
// if (getEventType() != TEXT) return "";
// String result = getText();
// next();
// return result;
// }
public String nextText() throws XmlPullParserException, IOException
{
// String result = null;
// boolean onStartTag = false;
// if(eventType == START_TAG) {
// onStartTag = true;
// next();
// }
// if(eventType == TEXT) {
// result = getText();
// next();
// } else if(onStartTag && eventType == END_TAG) {
// result = "";
// } else {
// throw new XmlPullParserException(
// "parser must be on START_TAG or TEXT to read text", this, null);
// }
// if(eventType != END_TAG) {
// throw new XmlPullParserException(
// "event TEXT it must be immediately followed by END_TAG", this, null);
// }
// return result;
if(getEventType() != START_TAG) {
throw new XmlPullParserException(
"parser must be on START_TAG to read next text", this, null);
}
int eventType = next();
if(eventType == TEXT) {
final String result = getText();
eventType = next();
if(eventType != END_TAG) {
throw new XmlPullParserException(
"TEXT must be immediately followed by END_TAG and not "
+TYPES[ getEventType() ], this, null);
}
return result;
} else if(eventType == END_TAG) {
return "";
} else {
throw new XmlPullParserException(
"parser must be on START_TAG or TEXT to read text", this, null);
}
}
public int nextTag() throws XmlPullParserException, IOException
{
next();
if(eventType == TEXT && isWhitespace()) { // skip whitespace
next();
}
if (eventType != START_TAG && eventType != END_TAG) {
throw new XmlPullParserException("expected START_TAG or END_TAG not "
+TYPES[ getEventType() ], this, null);
}
return eventType;
}
public int next()
throws XmlPullParserException, IOException
{
tokenize = false;
return nextImpl();
}
public int nextToken()
throws XmlPullParserException, IOException
{
tokenize = true;
return nextImpl();
}
protected int nextImpl()
throws XmlPullParserException, IOException
{
text = null;
pcEnd = pcStart = 0;
usePC = false;
bufStart = posEnd;
if(pastEndTag) {
pastEndTag = false;
--depth;
namespaceEnd = elNamespaceCount[ depth ]; // less namespaces available
}
if(emptyElementTag) {
emptyElementTag = false;
pastEndTag = true;
return eventType = END_TAG;
}
// [1] document ::= prolog element Misc*
if(depth > 0) {
if(seenStartTag) {
seenStartTag = false;
return eventType = parseStartTag();
}
if(seenEndTag) {
seenEndTag = false;
return eventType = parseEndTag();
}
// ASSUMPTION: we are _on_ first character of content or markup!!!!
// [43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)*
char ch;
if(seenMarkup) { // we have read ahead ...
seenMarkup = false;
ch = '<';
} else if(seenAmpersand) {
seenAmpersand = false;
ch = '&';
} else {
ch = more();
}
posStart = pos - 1; // VERY IMPORTANT: this is correct start of event!!!
// when true there is some potential event TEXT to return - keep gathering
boolean hadCharData = false;
// when true TEXT data is not continuous (like ) and requires PC merging
boolean needsMerging = false;
MAIN_LOOP:
while(true) {
// work on MARKUP
if(ch == '<') {
if(hadCharData) {
//posEnd = pos - 1;
if(tokenize) {
seenMarkup = true;
return eventType = TEXT;
}
}
ch = more();
if(ch == '/') {
if(!tokenize && hadCharData) {
seenEndTag = true;
//posEnd = pos - 2;
return eventType = TEXT;
}
return eventType = parseEndTag();
} else if(ch == '!') {
ch = more();
if(ch == '-') {
// note: if(tokenize == false) posStart/End is NOT changed!!!!
parseComment();
if(tokenize) return eventType = COMMENT;
if( !usePC && hadCharData ) {
needsMerging = true;
} else {
posStart = pos; //completely ignore comment
}
} else if(ch == '[') {
//posEnd = pos - 3;
// must remember previous posStart/End as it merges with content of CDATA
//int oldStart = posStart + bufAbsoluteStart;
//int oldEnd = posEnd + bufAbsoluteStart;
parseCDSect(hadCharData);
if(tokenize) return eventType = CDSECT;
final int cdStart = posStart;
final int cdEnd = posEnd;
final int cdLen = cdEnd - cdStart;
if(cdLen > 0) { // was there anything inside CDATA section?
hadCharData = true;
if(!usePC) {
needsMerging = true;
}
}
// posStart = oldStart;
// posEnd = oldEnd;
// if(cdLen > 0) { // was there anything inside CDATA section?
// if(hadCharData) {
// // do merging if there was anything in CDSect!!!!
// // if(!usePC) {
// // // posEnd is correct already!!!
// // if(posEnd > posStart) {
// // joinPC();
// // } else {
// // usePC = true;
// // pcStart = pcEnd = 0;
// // }
// // }
// // if(pcEnd + cdLen >= pc.length) ensurePC(pcEnd + cdLen);
// // // copy [cdStart..cdEnd) into PC
// // System.arraycopy(buf, cdStart, pc, pcEnd, cdLen);
// // pcEnd += cdLen;
// if(!usePC) {
// needsMerging = true;
// posStart = cdStart;
// posEnd = cdEnd;
// }
// } else {
// if(!usePC) {
// needsMerging = true;
// posStart = cdStart;
// posEnd = cdEnd;
// hadCharData = true;
// }
// }
// //hadCharData = true;
// } else {
// if( !usePC && hadCharData ) {
// needsMerging = true;
// }
// }
} else {
throw new XmlPullParserException(
"unexpected character in markup "+printable(ch), this, null);
}
} else if(ch == '?') {
parsePI();
if(tokenize) return eventType = PROCESSING_INSTRUCTION;
if( !usePC && hadCharData ) {
needsMerging = true;
} else {
posStart = pos; //completely ignore PI
}
} else if( isNameStartChar(ch) ) {
if(!tokenize && hadCharData) {
seenStartTag = true;
//posEnd = pos - 2;
return eventType = TEXT;
}
return eventType = parseStartTag();
} else {
throw new XmlPullParserException(
"unexpected character in markup "+printable(ch), this, null);
}
// do content comapctation if it makes sense!!!!
} else if(ch == '&') {
// work on ENTITTY
//posEnd = pos - 1;
if(tokenize && hadCharData) {
seenAmpersand = true;
return eventType = TEXT;
}
final int oldStart = posStart + bufAbsoluteStart;
final int oldEnd = posEnd + bufAbsoluteStart;
final char[] resolvedEntity = parseEntityRef();
if(tokenize) return eventType = ENTITY_REF;
// check if replacement text can be resolved !!!
if(resolvedEntity == null) {
if(entityRefName == null) {
entityRefName = newString(buf, posStart, posEnd - posStart);
}
throw new XmlPullParserException(
"could not resolve entity named '"+printable(entityRefName)+"'",
this, null);
}
//int entStart = posStart;
//int entEnd = posEnd;
posStart = oldStart - bufAbsoluteStart;
posEnd = oldEnd - bufAbsoluteStart;
if(!usePC) {
if(hadCharData) {
joinPC(); // posEnd is already set correctly!!!
needsMerging = false;
} else {
usePC = true;
pcStart = pcEnd = 0;
}
}
//assert usePC == true;
// write into PC replacement text - do merge for replacement text!!!!
for (int i = 0; i < resolvedEntity.length; i++)
{
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = resolvedEntity[ i ];
}
hadCharData = true;
//assert needsMerging == false;
} else {
if(needsMerging) {
//assert usePC == false;
joinPC(); // posEnd is already set correctly!!!
//posStart = pos - 1;
needsMerging = false;
}
//no MARKUP not ENTITIES so work on character data ...
// [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
hadCharData = true;
boolean normalizedCR = false;
final boolean normalizeInput = tokenize == false || roundtripSupported == false;
// use loop locality here!!!!
boolean seenBracket = false;
boolean seenBracketBracket = false;
do {
// check that ]]> does not show in
if(ch == ']') {
if(seenBracket) {
seenBracketBracket = true;
} else {
seenBracket = true;
}
} else if(seenBracketBracket && ch == '>') {
throw new XmlPullParserException(
"characters ]]> are not allowed in content", this, null);
} else {
if(seenBracket) {
seenBracketBracket = seenBracket = false;
}
// assert seenTwoBrackets == seenBracket == false;
}
if(normalizeInput) {
// deal with normalization issues ...
if(ch == '\r') {
normalizedCR = true;
posEnd = pos -1;
// posEnd is already set
if(!usePC) {
if(posEnd > posStart) {
joinPC();
} else {
usePC = true;
pcStart = pcEnd = 0;
}
}
//assert usePC == true;
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
} else if(ch == '\n') {
// if(!usePC) { joinPC(); } else { if(pcEnd >= pc.length) ensurePC(); }
if(!normalizedCR && usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
}
normalizedCR = false;
} else {
if(usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = ch;
}
normalizedCR = false;
}
}
ch = more();
} while(ch != '<' && ch != '&');
posEnd = pos - 1;
continue MAIN_LOOP; // skip ch = more() from below - we are already ahead ...
}
ch = more();
} // endless while(true)
} else {
if(seenRoot) {
return parseEpilog();
} else {
return parseProlog();
}
}
}
protected int parseProlog()
throws XmlPullParserException, IOException
{
// [2] prolog: ::= XMLDecl? Misc* (doctypedecl Misc*)? and look for [39] element
char ch;
if(seenMarkup) {
ch = buf[ pos - 1 ];
} else {
ch = more();
}
if(eventType == START_DOCUMENT) {
// bootstrap parsing with getting first character input!
// deal with BOM
// detect BOM and crop it (Unicode int Order Mark)
if(ch == '\uFFFE') {
throw new XmlPullParserException(
"first character in input was UNICODE noncharacter (0xFFFE)"+
"- input requires int swapping", this, null);
}
if(ch == '\uFEFF') {
// skipping UNICODE int Order Mark (so called BOM)
ch = more();
}
}
seenMarkup = false;
boolean gotS = false;
posStart = pos - 1;
final boolean normalizeIgnorableWS = tokenize == true && roundtripSupported == false;
boolean normalizedCR = false;
while(true) {
// deal with Misc
// [27] Misc ::= Comment | PI | S
// deal with docdecl --> mark it!
// else parseStartTag seen <[^/]
if(ch == '<') {
if(gotS && tokenize) {
posEnd = pos - 1;
seenMarkup = true;
return eventType = IGNORABLE_WHITESPACE;
}
ch = more();
if(ch == '?') {
// check if it is 'xml'
// deal with XMLDecl
boolean isXMLDecl = parsePI();
if(tokenize) {
if (isXMLDecl) {
return eventType = START_DOCUMENT;
}
return eventType = PROCESSING_INSTRUCTION;
}
} else if(ch == '!') {
ch = more();
if(ch == 'D') {
if(seenDocdecl) {
throw new XmlPullParserException(
"only one docdecl allowed in XML document", this, null);
}
seenDocdecl = true;
parseDocdecl();
if(tokenize) return eventType = DOCDECL;
} else if(ch == '-') {
parseComment();
if(tokenize) return eventType = COMMENT;
} else {
throw new XmlPullParserException(
"unexpected markup posStart) {
joinPC();
} else {
usePC = true;
pcStart = pcEnd = 0;
}
}
//assert usePC == true;
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
} else if(ch == '\n') {
if(!normalizedCR && usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
}
normalizedCR = false;
} else {
if(usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = ch;
}
normalizedCR = false;
}
}
} else {
throw new XmlPullParserException(
"only whitespace content allowed before start tag and not "+printable(ch),
this, null);
}
ch = more();
}
}
protected int parseEpilog()
throws XmlPullParserException, IOException
{
if(eventType == END_DOCUMENT) {
throw new XmlPullParserException("already reached end of XML input", this, null);
}
if(reachedEnd) {
return eventType = END_DOCUMENT;
}
boolean gotS = false;
final boolean normalizeIgnorableWS = tokenize == true && roundtripSupported == false;
boolean normalizedCR = false;
try {
// epilog: Misc*
char ch;
if(seenMarkup) {
ch = buf[ pos - 1 ];
} else {
ch = more();
}
seenMarkup = false;
posStart = pos - 1;
if(!reachedEnd) {
while(true) {
// deal with Misc
// [27] Misc ::= Comment | PI | S
if(ch == '<') {
if(gotS && tokenize) {
posEnd = pos - 1;
seenMarkup = true;
return eventType = IGNORABLE_WHITESPACE;
}
ch = more();
if(reachedEnd) {
break;
}
if(ch == '?') {
// check if it is 'xml'
// deal with XMLDecl
parsePI();
if(tokenize) return eventType = PROCESSING_INSTRUCTION;
} else if(ch == '!') {
ch = more();
if(reachedEnd) {
break;
}
if(ch == 'D') {
parseDocdecl(); //FIXME
if(tokenize) return eventType = DOCDECL;
} else if(ch == '-') {
parseComment();
if(tokenize) return eventType = COMMENT;
} else {
throw new XmlPullParserException(
"unexpected markup posStart) {
joinPC();
} else {
usePC = true;
pcStart = pcEnd = 0;
}
}
//assert usePC == true;
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
} else if(ch == '\n') {
if(!normalizedCR && usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
}
normalizedCR = false;
} else {
if(usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = ch;
}
normalizedCR = false;
}
}
} else {
throw new XmlPullParserException(
"in epilog non whitespace content is not allowed but got "+printable(ch),
this, null);
}
ch = more();
if(reachedEnd) {
break;
}
}
}
// throw Exception("unexpected content in epilog
// catch EOFException return END_DOCUMENT
//try {
} catch(EOFException ex) {
reachedEnd = true;
}
if(reachedEnd) {
if(tokenize && gotS) {
posEnd = pos; // well - this is LAST available character pos
return eventType = IGNORABLE_WHITESPACE;
}
return eventType = END_DOCUMENT;
} else {
throw new XmlPullParserException("internal error in parseEpilog");
}
}
public int parseEndTag() throws XmlPullParserException, IOException {
//ASSUMPTION ch is past ""
// [42] ETag ::= '' Name S? '>'
char ch = more();
if(!isNameStartChar(ch)) {
throw new XmlPullParserException(
"expected name start and not "+printable(ch), this, null);
}
posStart = pos - 3;
final int nameStart = pos - 1 + bufAbsoluteStart;
do {
ch = more();
} while(isNameChar(ch));
// now we go one level down -- do checks
//--depth; //FIXME
// check that end tag name is the same as start tag
//String name = new String(buf, nameStart - bufAbsoluteStart,
// (pos - 1) - (nameStart - bufAbsoluteStart));
//int last = pos - 1;
int off = nameStart - bufAbsoluteStart;
//final int len = last - off;
final int len = (pos - 1) - off;
final char[] cbuf = elRawName[depth];
if(elRawNameEnd[depth] != len) {
// construct strings for exception
final String startname = new String(cbuf, 0, elRawNameEnd[depth]);
final String endname = new String(buf, off, len);
throw new XmlPullParserException(
"end tag name "+endname+"> must match start tag name <"+startname+">"
+" from line "+elRawNameLine[depth], this, null);
}
for (int i = 0; i < len; i++)
{
if(buf[off++] != cbuf[i]) {
// construct strings for exception
final String startname = new String(cbuf, 0, len);
final String endname = new String(buf, off - i - 1, len);
throw new XmlPullParserException(
"end tag name "+endname+"> must be the same as start tag <"+startname+">"
+" from line "+elRawNameLine[depth], this, null);
}
}
while(isS(ch)) { ch = more(); } // skip additional white spaces
if(ch != '>') {
throw new XmlPullParserException(
"expected > to finsh end tag not "+printable(ch)
+" from line "+elRawNameLine[depth], this, null);
}
//namespaceEnd = elNamespaceCount[ depth ]; //FIXME
posEnd = pos;
pastEndTag = true;
return eventType = END_TAG;
}
public int parseStartTag() throws XmlPullParserException, IOException {
//ASSUMPTION ch is past '
// [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
++depth; //FIXME
posStart = pos - 2;
emptyElementTag = false;
attributeCount = 0;
// retrieve name
final int nameStart = pos - 1 + bufAbsoluteStart;
int colonPos = -1;
char ch = buf[ pos - 1];
if(ch == ':' && processNamespaces) throw new XmlPullParserException(
"when namespaces processing enabled colon can not be at element name start",
this, null);
while(true) {
ch = more();
if(!isNameChar(ch)) break;
if(ch == ':' && processNamespaces) {
if(colonPos != -1) throw new XmlPullParserException(
"only one colon is allowed in name of element when namespaces are enabled",
this, null);
colonPos = pos - 1 + bufAbsoluteStart;
}
}
// retrieve name
ensureElementsCapacity();
//TODO check for efficient interning and then use elRawNameInterned!!!!
int elLen = (pos - 1) - (nameStart - bufAbsoluteStart);
if(elRawName[ depth ] == null || elRawName[ depth ].length < elLen) {
elRawName[ depth ] = new char[ 2 * elLen ];
}
System.arraycopy(buf, nameStart - bufAbsoluteStart, elRawName[ depth ], 0, elLen);
elRawNameEnd[ depth ] = elLen;
elRawNameLine[ depth ] = lineNumber;
String name = null;
// work on prefixes and namespace URI
String prefix = null;
if(processNamespaces) {
if(colonPos != -1) {
prefix = elPrefix[ depth ] = newString(buf, nameStart - bufAbsoluteStart,
colonPos - nameStart);
name = elName[ depth ] = newString(buf, colonPos + 1 - bufAbsoluteStart,
//(pos -1) - (colonPos + 1));
pos - 2 - (colonPos - bufAbsoluteStart));
} else {
prefix = elPrefix[ depth ] = null;
name = elName[ depth ] = newString(buf, nameStart - bufAbsoluteStart, elLen);
}
} else {
name = elName[ depth ] = newString(buf, nameStart - bufAbsoluteStart, elLen);
}
while(true) {
while(isS(ch)) { ch = more(); } // skip additional white spaces
if(ch == '>') {
break;
} else if(ch == '/') {
if(emptyElementTag) throw new XmlPullParserException(
"repeated / in tag declaration", this, null);
emptyElementTag = true;
ch = more();
if(ch != '>') throw new XmlPullParserException(
"expected > to end empty tag not "+printable(ch), this, null);
break;
} else if(isNameStartChar(ch)) {
ch = parseAttribute();
ch = more();
continue;
} else {
throw new XmlPullParserException(
"start tag unexpected character "+printable(ch), this, null);
}
//ch = more(); // skip space
}
// now when namespaces were declared we can resolve them
if(processNamespaces) {
String uri = getNamespace(prefix);
if(uri == null) {
if(prefix == null) { // no prefix and no uri => use default namespace
uri = NO_NAMESPACE;
} else {
throw new XmlPullParserException(
"could not determine namespace bound to element prefix "+prefix,
this, null);
}
}
elUri[ depth ] = uri;
//String uri = getNamespace(prefix);
//if(uri == null && prefix == null) { // no prefix and no uri => use default namespace
// uri = "";
//}
// resolve attribute namespaces
for (int i = 0; i < attributeCount; i++)
{
final String attrPrefix = attributePrefix[ i ];
if(attrPrefix != null) {
final String attrUri = getNamespace(attrPrefix);
if(attrUri == null) {
throw new XmlPullParserException(
"could not determine namespace bound to attribute prefix "+attrPrefix,
this, null);
}
attributeUri[ i ] = attrUri;
} else {
attributeUri[ i ] = NO_NAMESPACE;
}
}
//TODO
//[ WFC: Unique Att Spec ]
// check namespaced attribute uniqueness contraint!!!
for (int i = 1; i < attributeCount; i++)
{
for (int j = 0; j < i; j++)
{
if( attributeUri[j] == attributeUri[i]
&& (allStringsInterned && attributeName[j].equals(attributeName[i])
|| (!allStringsInterned
&& attributeNameHash[ j ] == attributeNameHash[ i ]
&& attributeName[j].equals(attributeName[i])) )
) {
// prepare data for nice error messgae?
String attr1 = attributeName[j];
if(attributeUri[j] != null) attr1 = attributeUri[j]+":"+attr1;
String attr2 = attributeName[i];
if(attributeUri[i] != null) attr2 = attributeUri[i]+":"+attr2;
throw new XmlPullParserException(
"duplicated attributes "+attr1+" and "+attr2, this, null);
}
}
}
} else { // ! processNamespaces
//[ WFC: Unique Att Spec ]
// check raw attribute uniqueness contraint!!!
for (int i = 1; i < attributeCount; i++)
{
for (int j = 0; j < i; j++)
{
if((allStringsInterned && attributeName[j].equals(attributeName[i])
|| (!allStringsInterned
&& attributeNameHash[ j ] == attributeNameHash[ i ]
&& attributeName[j].equals(attributeName[i])) )
) {
// prepare data for nice error messgae?
final String attr1 = attributeName[j];
final String attr2 = attributeName[i];
throw new XmlPullParserException(
"duplicated attributes "+attr1+" and "+attr2, this, null);
}
}
}
}
elNamespaceCount[ depth ] = namespaceEnd;
posEnd = pos;
return eventType = START_TAG;
}
protected char parseAttribute() throws XmlPullParserException, IOException
{
// parse attribute
// [41] Attribute ::= Name Eq AttValue
// [WFC: No External Entity References]
// [WFC: No < in Attribute Values]
final int prevPosStart = posStart + bufAbsoluteStart;
final int nameStart = pos - 1 + bufAbsoluteStart;
int colonPos = -1;
char ch = buf[ pos - 1 ];
if(ch == ':' && processNamespaces) throw new XmlPullParserException(
"when namespaces processing enabled colon can not be at attribute name start",
this, null);
boolean startsWithXmlns = processNamespaces && ch == 'x';
int xmlnsPos = 0;
ch = more();
while(isNameChar(ch)) {
if(processNamespaces) {
if(startsWithXmlns && xmlnsPos < 5) {
++xmlnsPos;
if(xmlnsPos == 1) { if(ch != 'm') startsWithXmlns = false; }
else if(xmlnsPos == 2) { if(ch != 'l') startsWithXmlns = false; }
else if(xmlnsPos == 3) { if(ch != 'n') startsWithXmlns = false; }
else if(xmlnsPos == 4) { if(ch != 's') startsWithXmlns = false; }
else if(xmlnsPos == 5) {
if(ch != ':') throw new XmlPullParserException(
"after xmlns in attribute name must be colon"
+"when namespaces are enabled", this, null);
//colonPos = pos - 1 + bufAbsoluteStart;
}
}
if(ch == ':') {
if(colonPos != -1) throw new XmlPullParserException(
"only one colon is allowed in attribute name"
+" when namespaces are enabled", this, null);
colonPos = pos - 1 + bufAbsoluteStart;
}
}
ch = more();
}
ensureAttributesCapacity(attributeCount);
// --- start processing attributes
String name = null;
String prefix = null;
// work on prefixes and namespace URI
if(processNamespaces) {
if(xmlnsPos < 4) startsWithXmlns = false;
if(startsWithXmlns) {
if(colonPos != -1) {
//prefix = attributePrefix[ attributeCount ] = null;
final int nameLen = pos - 2 - (colonPos - bufAbsoluteStart);
if(nameLen == 0) {
throw new XmlPullParserException(
"namespace prefix is required after xmlns: "
+" when namespaces are enabled", this, null);
}
name = //attributeName[ attributeCount ] =
newString(buf, colonPos - bufAbsoluteStart + 1, nameLen);
//pos - 1 - (colonPos + 1 - bufAbsoluteStart)
}
} else {
if(colonPos != -1) {
int prefixLen = colonPos - nameStart;
prefix = attributePrefix[ attributeCount ] =
newString(buf, nameStart - bufAbsoluteStart,prefixLen);
//colonPos - (nameStart - bufAbsoluteStart));
int nameLen = pos - 2 - (colonPos - bufAbsoluteStart);
name = attributeName[ attributeCount ] =
newString(buf, colonPos - bufAbsoluteStart + 1, nameLen);
//pos - 1 - (colonPos + 1 - bufAbsoluteStart));
//name.substring(0, colonPos-nameStart);
} else {
prefix = attributePrefix[ attributeCount ] = null;
name = attributeName[ attributeCount ] =
newString(buf, nameStart - bufAbsoluteStart,
pos - 1 - (nameStart - bufAbsoluteStart));
}
if(!allStringsInterned) {
attributeNameHash[ attributeCount ] = name.hashCode();
}
}
} else {
// retrieve name
name = attributeName[ attributeCount ] =
newString(buf, nameStart - bufAbsoluteStart,
pos - 1 - (nameStart - bufAbsoluteStart));
////assert name != null;
if(!allStringsInterned) {
attributeNameHash[ attributeCount ] = name.hashCode();
}
}
// [25] Eq ::= S? '=' S?
while(isS(ch)) { ch = more(); } // skip additional spaces
if(ch != '=') throw new XmlPullParserException(
"expected = after attribute name", this, null);
ch = more();
while(isS(ch)) { ch = more(); } // skip additional spaces
// [10] AttValue ::= '"' ([^<&"] | Reference)* '"'
// | "'" ([^<&'] | Reference)* "'"
final char delimit = ch;
if(delimit != '"' && delimit != '\'') throw new XmlPullParserException(
"attribute value must start with quotation or apostrophe not "
+printable(delimit), this, null);
// parse until delimit or < and resolve Reference
//[67] Reference ::= EntityRef | CharRef
//int valueStart = pos + bufAbsoluteStart;
boolean normalizedCR = false;
usePC = false;
pcStart = pcEnd;
posStart = pos;
while(true) {
ch = more();
if(ch == delimit) {
break;
} if(ch == '<') {
throw new XmlPullParserException(
"markup not allowed inside attribute value - illegal < ", this, null);
} if(ch == '&') {
// extractEntityRef
posEnd = pos - 1;
if(!usePC) {
final boolean hadCharData = posEnd > posStart;
if(hadCharData) {
// posEnd is already set correctly!!!
joinPC();
} else {
usePC = true;
pcStart = pcEnd = 0;
}
}
//assert usePC == true;
final char[] resolvedEntity = parseEntityRef();
// check if replacement text can be resolved !!!
if(resolvedEntity == null) {
if(entityRefName == null) {
entityRefName = newString(buf, posStart, posEnd - posStart);
}
throw new XmlPullParserException(
"could not resolve entity named '"+printable(entityRefName)+"'",
this, null);
}
// write into PC replacement text - do merge for replacement text!!!!
for (int i = 0; i < resolvedEntity.length; i++)
{
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = resolvedEntity[ i ];
}
} else if(ch == '\t' || ch == '\n' || ch == '\r') {
// do attribute value normalization
// as described in http://www.w3.org/TR/REC-xml#AVNormalize
// TODO add test for it form spec ...
// handle EOL normalization ...
if(!usePC) {
posEnd = pos - 1;
if(posEnd > posStart) {
joinPC();
} else {
usePC = true;
pcEnd = pcStart = 0;
}
}
//assert usePC == true;
if(pcEnd >= pc.length) ensurePC(pcEnd);
if(ch != '\n' || !normalizedCR) {
pc[pcEnd++] = ' '; //'\n';
}
} else {
if(usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = ch;
}
}
normalizedCR = ch == '\r';
}
if(processNamespaces && startsWithXmlns) {
String ns = null;
if(!usePC) {
ns = newStringIntern(buf, posStart, pos - 1 - posStart);
} else {
ns = newStringIntern(pc, pcStart, pcEnd - pcStart);
}
ensureNamespacesCapacity(namespaceEnd);
int prefixHash = -1;
if(colonPos != -1) {
if(ns.length() == 0) {
throw new XmlPullParserException(
"non-default namespace can not be declared to be empty string", this, null);
}
// declare new namespace
namespacePrefix[ namespaceEnd ] = name;
if(!allStringsInterned) {
prefixHash = namespacePrefixHash[ namespaceEnd ] = name.hashCode();
}
} else {
// declare new default namespace...
namespacePrefix[ namespaceEnd ] = null; //""; //null; //TODO check FIXME Alek
if(!allStringsInterned) {
prefixHash = namespacePrefixHash[ namespaceEnd ] = -1;
}
}
namespaceUri[ namespaceEnd ] = ns;
// detect duplicate namespace declarations!!!
final int startNs = elNamespaceCount[ depth - 1 ];
for (int i = namespaceEnd - 1; i >= startNs; --i)
{
if(((allStringsInterned || name == null) && namespacePrefix[ i ] == name)
|| (!allStringsInterned && name != null &&
namespacePrefixHash[ i ] == prefixHash
&& name.equals(namespacePrefix[ i ])
))
{
final String s = name == null ? "default" : "'"+name+"'";
throw new XmlPullParserException(
"duplicated namespace declaration for "+s+" prefix", this, null);
}
}
++namespaceEnd;
} else {
if(!usePC) {
attributeValue[ attributeCount ] =
new String(buf, posStart, pos - 1 - posStart);
} else {
attributeValue[ attributeCount ] =
new String(pc, pcStart, pcEnd - pcStart);
}
++attributeCount;
}
posStart = prevPosStart - bufAbsoluteStart;
return ch;
}
protected char[] charRefOneCharBuf = new char[1];
protected char[] parseEntityRef()
throws XmlPullParserException, IOException
{
// entity reference http://www.w3.org/TR/2000/REC-xml-20001006#NT-Reference
// [67] Reference ::= EntityRef | CharRef
// ASSUMPTION just after &
entityRefName = null;
posStart = pos;
char ch = more();
StringBuffer sb = new StringBuffer();
if(ch == '#') {
// parse character reference
char charRef = 0;
ch = more();
if(ch == 'x') {
//encoded in hex
while(true) {
ch = more();
if(ch >= '0' && ch <= '9') {
charRef = (char)(charRef * 16 + (ch - '0'));
sb.append( ch );
} else if(ch >= 'a' && ch <= 'f') {
charRef = (char)(charRef * 16 + (ch - ('a' - 10)));
sb.append( ch );
} else if(ch >= 'A' && ch <= 'F') {
charRef = (char)(charRef * 16 + (ch - ('A' - 10)));
sb.append( ch );
} else if(ch == ';') {
break;
} else {
throw new XmlPullParserException(
"character reference (with hex value) may not contain "
+printable(ch), this, null);
}
}
} else {
// encoded in decimal
while(true) {
if(ch >= '0' && ch <= '9') {
charRef = (char)(charRef * 10 + (ch - '0'));
} else if(ch == ';') {
break;
} else {
throw new XmlPullParserException(
"character reference (with decimal value) may not contain "
+printable(ch), this, null);
}
ch = more();
}
}
posEnd = pos - 1;
if ( sb.length() > 0 )
{
char[] tmp = toChars( Integer.parseInt( sb.toString(), 16 ) );
charRefOneCharBuf = tmp;
if ( tokenize )
{
text = newString( charRefOneCharBuf, 0, charRefOneCharBuf.length );
}
return charRefOneCharBuf;
}
charRefOneCharBuf[0] = charRef;
if(tokenize) {
text = newString(charRefOneCharBuf, 0, 1);
}
return charRefOneCharBuf;
} else {
// [68] EntityRef ::= '&' Name ';'
// scan anem until ;
if(!isNameStartChar(ch)) {
throw new XmlPullParserException(
"entity reference names can not start with character '"
+printable(ch)+"'", this, null);
}
while(true) {
ch = more();
if(ch == ';') {
break;
}
if(!isNameChar(ch)) {
throw new XmlPullParserException(
"entity reference name can not contain character "
+printable(ch)+"'", this, null);
}
}
posEnd = pos - 1;
// determine what name maps to
final int len = posEnd - posStart;
if(len == 2 && buf[posStart] == 'l' && buf[posStart+1] == 't') {
if(tokenize) {
text = "<";
}
charRefOneCharBuf[0] = '<';
return charRefOneCharBuf;
//if(paramPC || isParserTokenizing) {
// if(pcEnd >= pc.length) ensurePC();
// pc[pcEnd++] = '<';
//}
} else if(len == 3 && buf[posStart] == 'a'
&& buf[posStart+1] == 'm' && buf[posStart+2] == 'p') {
if(tokenize) {
text = "&";
}
charRefOneCharBuf[0] = '&';
return charRefOneCharBuf;
} else if(len == 2 && buf[posStart] == 'g' && buf[posStart+1] == 't') {
if(tokenize) {
text = ">";
}
charRefOneCharBuf[0] = '>';
return charRefOneCharBuf;
} else if(len == 4 && buf[posStart] == 'a' && buf[posStart+1] == 'p'
&& buf[posStart+2] == 'o' && buf[posStart+3] == 's')
{
if(tokenize) {
text = "'";
}
charRefOneCharBuf[0] = '\'';
return charRefOneCharBuf;
} else if(len == 4 && buf[posStart] == 'q' && buf[posStart+1] == 'u'
&& buf[posStart+2] == 'o' && buf[posStart+3] == 't')
{
if(tokenize) {
text = "\"";
}
charRefOneCharBuf[0] = '"';
return charRefOneCharBuf;
} else {
final char[] result = lookuEntityReplacement(len);
if(result != null) {
return result;
}
}
if(tokenize) text = null;
return null;
}
}
protected char[] lookuEntityReplacement(int entitNameLen)
throws XmlPullParserException, IOException
{
if(!allStringsInterned) {
final int hash = fastHash(buf, posStart, posEnd - posStart);
LOOP:
for (int i = entityEnd - 1; i >= 0; --i)
{
if(hash == entityNameHash[ i ] && entitNameLen == entityNameBuf[ i ].length) {
final char[] entityBuf = entityNameBuf[ i ];
for (int j = 0; j < entitNameLen; j++)
{
if(buf[posStart + j] != entityBuf[j]) continue LOOP;
}
if(tokenize) text = entityReplacement[ i ];
return entityReplacementBuf[ i ];
}
}
} else {
entityRefName = newString(buf, posStart, posEnd - posStart);
for (int i = entityEnd - 1; i >= 0; --i)
{
// take advantage that interning for newStirng is enforced
if(entityRefName == entityName[ i ]) {
if(tokenize) text = entityReplacement[ i ];
return entityReplacementBuf[ i ];
}
}
}
return null;
}
protected void parseComment()
throws XmlPullParserException, IOException
{
// implements XML 1.0 Section 2.5 Comments
//ASSUMPTION: seen
ch = more();
if(seenDashDash && ch != '>') {
throw new XmlPullParserException(
"in comment after two dashes (--) next character must be >"
+" not "+printable(ch), this, null);
}
if(ch == '-') {
if(!seenDash) {
seenDash = true;
} else {
seenDashDash = true;
seenDash = false;
}
} else if(ch == '>') {
if(seenDashDash) {
break; // found end sequence!!!!
} else {
seenDashDash = false;
}
seenDash = false;
} else {
seenDash = false;
}
if(normalizeIgnorableWS) {
if(ch == '\r') {
normalizedCR = true;
//posEnd = pos -1;
//joinPC();
// posEnd is alreadys set
if(!usePC) {
posEnd = pos -1;
if(posEnd > posStart) {
joinPC();
} else {
usePC = true;
pcStart = pcEnd = 0;
}
}
//assert usePC == true;
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
} else if(ch == '\n') {
if(!normalizedCR && usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
}
normalizedCR = false;
} else {
if(usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = ch;
}
normalizedCR = false;
}
}
}
} catch(EOFException ex) {
// detect EOF and create meaningful error ...
throw new XmlPullParserException(
"comment started on line "+curLine+" and column "+curColumn+" was not closed",
this, ex);
}
if(tokenize) {
posEnd = pos - 3;
if(usePC) {
pcEnd -= 2;
}
}
}
protected boolean parsePI()
throws XmlPullParserException, IOException
{
// implements XML 1.0 Section 2.6 Processing Instructions
// [16] PI ::= '' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
// [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
//ASSUMPTION: seen
if(tokenize) posStart = pos;
final int curLine = lineNumber;
final int curColumn = columnNumber;
int piTargetStart = pos + bufAbsoluteStart;
int piTargetEnd = -1;
final boolean normalizeIgnorableWS = tokenize == true && roundtripSupported == false;
boolean normalizedCR = false;
try {
boolean seenQ = false;
char ch = more();
if(isS(ch)) {
throw new XmlPullParserException(
"processing instruction PITarget must be exactly after and not white space character",
this, null);
}
while(true) {
// scan until it hits ?>
//ch = more();
if(ch == '?') {
seenQ = true;
} else if(ch == '>') {
if(seenQ) {
break; // found end sequence!!!!
}
seenQ = false;
} else {
if(piTargetEnd == -1 && isS(ch)) {
piTargetEnd = pos - 1 + bufAbsoluteStart;
// [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
if((piTargetEnd - piTargetStart) == 3) {
if((buf[piTargetStart] == 'x' || buf[piTargetStart] == 'X')
&& (buf[piTargetStart+1] == 'm' || buf[piTargetStart+1] == 'M')
&& (buf[piTargetStart+2] == 'l' || buf[piTargetStart+2] == 'L')
)
{
if(piTargetStart > 3) { // posStart) {
joinPC();
} else {
usePC = true;
pcStart = pcEnd = 0;
}
}
//assert usePC == true;
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
} else if(ch == '\n') {
if(!normalizedCR && usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
}
normalizedCR = false;
} else {
if(usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = ch;
}
normalizedCR = false;
}
}
ch = more();
}
} catch(EOFException ex) {
// detect EOF and create meaningful error ...
throw new XmlPullParserException(
"processing instruction started on line "+curLine+" and column "+curColumn
+" was not closed",
this, ex);
}
if(piTargetEnd == -1) {
piTargetEnd = pos - 2 + bufAbsoluteStart;
//throw new XmlPullParserException(
// "processing instruction must have PITarget name", this, null);
}
piTargetStart -= bufAbsoluteStart;
piTargetEnd -= bufAbsoluteStart;
if(tokenize) {
posEnd = pos - 2;
if(normalizeIgnorableWS) {
--pcEnd;
}
}
return true;
}
// protected final static char[] VERSION = {'v','e','r','s','i','o','n'};
// protected final static char[] NCODING = {'n','c','o','d','i','n','g'};
// protected final static char[] TANDALONE = {'t','a','n','d','a','l','o','n','e'};
// protected final static char[] YES = {'y','e','s'};
// protected final static char[] NO = {'n','o'};
protected final static char[] VERSION = "version".toCharArray();
protected final static char[] NCODING = "ncoding".toCharArray();
protected final static char[] TANDALONE = "tandalone".toCharArray();
protected final static char[] YES = "yes".toCharArray();
protected final static char[] NO = "no".toCharArray();
protected void parseXmlDecl(char ch)
throws XmlPullParserException, IOException
{
// [23] XMLDecl ::= ''
// first make sure that relative positions will stay OK
preventBufferCompaction = true;
bufStart = 0; // necessary to keep pos unchanged during expansion!
// --- parse VersionInfo
// [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
// parse is positioned just on first S past 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9')
&& ch != '_' && ch != '.' && ch != ':' && ch != '-')
{
throw new XmlPullParserException(
" 'z') && (ch < 'A' || ch > 'Z'))
{
throw new XmlPullParserException(
" 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9')
&& ch != '.' && ch != '_' && ch != '-')
{
throw new XmlPullParserException(
" as last part of ') {
throw new XmlPullParserException(
"expected ?> as last part of '
int bracketLevel = 0;
final boolean normalizeIgnorableWS = tokenize == true && roundtripSupported == false;
boolean normalizedCR = false;
while(true) {
ch = more();
if(ch == '[') ++bracketLevel;
if(ch == ']') --bracketLevel;
if(ch == '>' && bracketLevel == 0) break;
if(normalizeIgnorableWS) {
if(ch == '\r') {
normalizedCR = true;
//posEnd = pos -1;
//joinPC();
// posEnd is alreadys set
if(!usePC) {
posEnd = pos -1;
if(posEnd > posStart) {
joinPC();
} else {
usePC = true;
pcStart = pcEnd = 0;
}
}
//assert usePC == true;
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
} else if(ch == '\n') {
if(!normalizedCR && usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
}
normalizedCR = false;
} else {
if(usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = ch;
}
normalizedCR = false;
}
}
}
posEnd = pos - 1;
}
protected void parseCDSect(boolean hadCharData)
throws XmlPullParserException, IOException
{
// implements XML 1.0 Section 2.7 CDATA Sections
// [18] CDSect ::= CDStart CData CDEnd
// [19] CDStart ::= '' Char*))
// [21] CDEnd ::= ']]>'
//ASSUMPTION: seen posStart) {
joinPC();
} else {
usePC = true;
pcStart = pcEnd = 0;
}
}
}
}
boolean seenBracket = false;
boolean seenBracketBracket = false;
boolean normalizedCR = false;
while(true) {
// scan until it hits "]]>"
ch = more();
if(ch == ']') {
if(!seenBracket) {
seenBracket = true;
} else {
seenBracketBracket = true;
//seenBracket = false;
}
} else if(ch == '>') {
if(seenBracket && seenBracketBracket) {
break; // found end sequence!!!!
} else {
seenBracketBracket = false;
}
seenBracket = false;
} else {
if(seenBracket) {
seenBracket = false;
}
}
if(normalizeInput) {
// deal with normalization issues ...
if(ch == '\r') {
normalizedCR = true;
posStart = cdStart - bufAbsoluteStart;
posEnd = pos - 1; // posEnd is alreadys set
if(!usePC) {
if(posEnd > posStart) {
joinPC();
} else {
usePC = true;
pcStart = pcEnd = 0;
}
}
//assert usePC == true;
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
} else if(ch == '\n') {
if(!normalizedCR && usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = '\n';
}
normalizedCR = false;
} else {
if(usePC) {
if(pcEnd >= pc.length) ensurePC(pcEnd);
pc[pcEnd++] = ch;
}
normalizedCR = false;
}
}
}
} catch(EOFException ex) {
// detect EOF and create meaningful error ...
throw new XmlPullParserException(
"CDATA section started on line "+curLine+" and column "+curColumn+" was not closed",
this, ex);
}
if(normalizeInput) {
if(usePC) {
pcEnd = pcEnd - 2;
}
}
posStart = cdStart - bufAbsoluteStart;
posEnd = pos - 3;
}
protected void fillBuf() throws IOException, XmlPullParserException {
if(reader == null) throw new XmlPullParserException(
"reader must be set before parsing is started");
// see if we are in compaction area
if(bufEnd > bufSoftLimit) {
// expand buffer it makes sense!!!!
boolean compact = bufStart > bufSoftLimit;
boolean expand = false;
if(preventBufferCompaction) {
compact = false;
expand = true;
} else if(!compact) {
//freeSpace
if(bufStart < buf.length / 2) {
// less then half buffer available forcompactin --> expand instead!!!
expand = true;
} else {
// at least half of buffer can be reclaimed --> worthwhile effort!!!
compact = true;
}
}
// if buffer almost full then compact it
if(compact) {
//TODO: look on trashing
// //assert bufStart > 0
System.arraycopy(buf, bufStart, buf, 0, bufEnd - bufStart);
if(TRACE_SIZING) System.out.println(
"TRACE_SIZING fillBuf() compacting "+bufStart
+" bufEnd="+bufEnd
+" pos="+pos+" posStart="+posStart+" posEnd="+posEnd
+" buf first 100 chars:"+new String(buf, bufStart,
bufEnd - bufStart < 100 ? bufEnd - bufStart : 100 ));
} else if(expand) {
final int newSize = 2 * buf.length;
final char newBuf[] = new char[ newSize ];
if(TRACE_SIZING) System.out.println("TRACE_SIZING fillBuf() "+buf.length+" => "+newSize);
System.arraycopy(buf, bufStart, newBuf, 0, bufEnd - bufStart);
buf = newBuf;
if(bufLoadFactor > 0) {
bufSoftLimit = ( bufLoadFactor * buf.length ) /100;
}
} else {
throw new XmlPullParserException("internal error in fillBuffer()");
}
bufEnd -= bufStart;
pos -= bufStart;
posStart -= bufStart;
posEnd -= bufStart;
bufAbsoluteStart += bufStart;
bufStart = 0;
if(TRACE_SIZING) System.out.println(
"TRACE_SIZING fillBuf() after bufEnd="+bufEnd
+" pos="+pos+" posStart="+posStart+" posEnd="+posEnd
+" buf first 100 chars:"+new String(buf, 0, bufEnd < 100 ? bufEnd : 100));
}
// at least one charcter must be read or error
final int len = buf.length - bufEnd > READ_CHUNK_SIZE ? READ_CHUNK_SIZE : buf.length - bufEnd;
final int ret = reader.read(buf, bufEnd, len);
if(ret > 0) {
bufEnd += ret;
if(TRACE_SIZING) System.out.println(
"TRACE_SIZING fillBuf() after filling in buffer"
+" buf first 100 chars:"+new String(buf, 0, bufEnd < 100 ? bufEnd : 100));
return;
}
if(ret == -1) {
if(bufAbsoluteStart == 0 && pos == 0) {
throw new EOFException("input contained no data");
} else {
if(seenRoot && depth == 0) { // inside parsing epilog!!!
reachedEnd = true;
return;
} else {
StringBuffer expectedTagStack = new StringBuffer();
if(depth > 0) {
//final char[] cbuf = elRawName[depth];
//final String startname = new String(cbuf, 0, elRawNameEnd[depth]);
expectedTagStack.append(" - expected end tag");
if(depth > 1) {
expectedTagStack.append("s"); //more than one end tag
}
expectedTagStack.append(" ");
for (int i = depth; i > 0; i--)
{
String tagName = new String(elRawName[i], 0, elRawNameEnd[i]);
expectedTagStack.append("").append(tagName).append('>');
}
expectedTagStack.append(" to close");
for (int i = depth; i > 0; i--)
{
if(i != depth) {
expectedTagStack.append(" and"); //more than one end tag
}
String tagName = new String(elRawName[i], 0, elRawNameEnd[i]);
expectedTagStack.append(" start tag <"+tagName+">");
expectedTagStack.append(" from line "+elRawNameLine[i]);
}
expectedTagStack.append(", parser stopped on");
}
throw new EOFException("no more data available"
+expectedTagStack.toString()+getPositionDescription());
}
}
} else {
throw new IOException("error reading input, returned "+ret);
}
}
protected char more() throws IOException, XmlPullParserException {
if(pos >= bufEnd) {
fillBuf();
// this return value should be ignonored as it is used in epilog parsing ...
if(reachedEnd) return (char)-1;
}
final char ch = buf[pos++];
//line/columnNumber
if(ch == '\n') { ++lineNumber; columnNumber = 1; }
else { ++columnNumber; }
//System.out.print(ch);
return ch;
}
// /**
// * This function returns position of parser in XML input stream
// * (how many characters were processed.
// * NOTE: this logical position and not byte offset as encodings
// * such as UTF8 may use more than one byte to encode one character.
// */
// public int getCurrentInputPosition() {
// return pos + bufAbsoluteStart;
// }
protected void ensurePC(int end) {
//assert end >= pc.length;
final int newSize = end > READ_CHUNK_SIZE ? 2 * end : 2 * READ_CHUNK_SIZE;
final char[] newPC = new char[ newSize ];
if(TRACE_SIZING) System.out.println("TRACE_SIZING ensurePC() "+pc.length+" ==> "+newSize+" end="+end);
System.arraycopy(pc, 0, newPC, 0, pcEnd);
pc = newPC;
//assert end < pc.length;
}
protected void joinPC() {
//assert usePC == false;
//assert posEnd > posStart;
final int len = posEnd - posStart;
final int newEnd = pcEnd + len + 1;
if(newEnd >= pc.length) ensurePC(newEnd); // add 1 for extra space for one char
//assert newEnd < pc.length;
System.arraycopy(buf, posStart, pc, pcEnd, len);
pcEnd += len;
usePC = true;
}
protected char requireInput(char ch, char[] input)
throws XmlPullParserException, IOException
{
for (int i = 0; i < input.length; i++)
{
if(ch != input[i]) {
throw new XmlPullParserException(
"expected "+printable(input[i])+" in "+new String(input)
+" and not "+printable(ch), this, null);
}
ch = more();
}
return ch;
}
protected char requireNextS()
throws XmlPullParserException, IOException
{
final char ch = more();
if(!isS(ch)) {
throw new XmlPullParserException(
"white space is required and not "+printable(ch), this, null);
}
return skipS(ch);
}
protected char skipS(char ch)
throws XmlPullParserException, IOException
{
while(isS(ch)) { ch = more(); } // skip additional spaces
return ch;
}
// nameStart / name lookup tables based on XML 1.1 http://www.w3.org/TR/2001/WD-xml11-20011213/
protected static final int LOOKUP_MAX = 0x400;
protected static final char LOOKUP_MAX_CHAR = (char)LOOKUP_MAX;
// protected static int lookupNameStartChar[] = new int[ LOOKUP_MAX_CHAR / 32 ];
// protected static int lookupNameChar[] = new int[ LOOKUP_MAX_CHAR / 32 ];
protected static boolean lookupNameStartChar[] = new boolean[ LOOKUP_MAX ];
protected static boolean lookupNameChar[] = new boolean[ LOOKUP_MAX ];
private static final void setName(char ch)
//{ lookupNameChar[ (int)ch / 32 ] |= (1 << (ch % 32)); }
{ lookupNameChar[ ch ] = true; }
private static final void setNameStart(char ch)
//{ lookupNameStartChar[ (int)ch / 32 ] |= (1 << (ch % 32)); setName(ch); }
{ lookupNameStartChar[ ch ] = true; setName(ch); }
static {
setNameStart(':');
for (char ch = 'A'; ch <= 'Z'; ++ch) setNameStart(ch);
setNameStart('_');
for (char ch = 'a'; ch <= 'z'; ++ch) setNameStart(ch);
for (char ch = '\u00c0'; ch <= '\u02FF'; ++ch) setNameStart(ch);
for (char ch = '\u0370'; ch <= '\u037d'; ++ch) setNameStart(ch);
for (char ch = '\u037f'; ch < '\u0400'; ++ch) setNameStart(ch);
setName('-');
setName('.');
for (char ch = '0'; ch <= '9'; ++ch) setName(ch);
setName('\u00b7');
for (char ch = '\u0300'; ch <= '\u036f'; ++ch) setName(ch);
}
//private final static boolean isNameStartChar(char ch) {
protected boolean isNameStartChar(char ch) {
return (ch < LOOKUP_MAX_CHAR && lookupNameStartChar[ ch ])
|| (ch >= LOOKUP_MAX_CHAR && ch <= '\u2027')
|| (ch >= '\u202A' && ch <= '\u218F')
|| (ch >= '\u2800' && ch <= '\uFFEF')
;
// if(ch < LOOKUP_MAX_CHAR) return lookupNameStartChar[ ch ];
// else return ch <= '\u2027'
// || (ch >= '\u202A' && ch <= '\u218F')
// || (ch >= '\u2800' && ch <= '\uFFEF')
// ;
//return false;
// return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == ':'
// || (ch >= '0' && ch <= '9');
// if(ch < LOOKUP_MAX_CHAR) return (lookupNameStartChar[ (int)ch / 32 ] & (1 << (ch % 32))) != 0;
// if(ch <= '\u2027') return true;
// //[#x202A-#x218F]
// if(ch < '\u202A') return false;
// if(ch <= '\u218F') return true;
// // added pairts [#x2800-#xD7FF] | [#xE000-#xFDCF] | [#xFDE0-#xFFEF] | [#x10000-#x10FFFF]
// if(ch < '\u2800') return false;
// if(ch <= '\uFFEF') return true;
// return false;
// else return (supportXml11 && ( (ch < '\u2027') || (ch > '\u2029' && ch < '\u2200') ...
}
//private final static boolean isNameChar(char ch) {
protected boolean isNameChar(char ch) {
//return isNameStartChar(ch);
// if(ch < LOOKUP_MAX_CHAR) return (lookupNameChar[ (int)ch / 32 ] & (1 << (ch % 32))) != 0;
return (ch < LOOKUP_MAX_CHAR && lookupNameChar[ ch ])
|| (ch >= LOOKUP_MAX_CHAR && ch <= '\u2027')
|| (ch >= '\u202A' && ch <= '\u218F')
|| (ch >= '\u2800' && ch <= '\uFFEF')
;
//return false;
// return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == ':'
// || (ch >= '0' && ch <= '9');
// if(ch < LOOKUP_MAX_CHAR) return (lookupNameStartChar[ (int)ch / 32 ] & (1 << (ch % 32))) != 0;
//else return
// else if(ch <= '\u2027') return true;
// //[#x202A-#x218F]
// else if(ch < '\u202A') return false;
// else if(ch <= '\u218F') return true;
// // added pairts [#x2800-#xD7FF] | [#xE000-#xFDCF] | [#xFDE0-#xFFEF] | [#x10000-#x10FFFF]
// else if(ch < '\u2800') return false;
// else if(ch <= '\uFFEF') return true;
//else return false;
}
protected boolean isS(char ch) {
return (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t');
// || (supportXml11 && (ch == '\u0085' || ch == '\u2028');
}
//protected boolean isChar(char ch) { return (ch < '\uD800' || ch > '\uDFFF')
// ch != '\u0000' ch < '\uFFFE'
//protected char printable(char ch) { return ch; }
protected String printable(char ch) {
if(ch == '\n') {
return "\\n";
} else if(ch == '\r') {
return "\\r";
} else if(ch == '\t') {
return "\\t";
} else if(ch == '\'') {
return "\\'";
} if(ch > 127 || ch < 32) {
return "\\u"+Integer.toHexString((int)ch);
}
return ""+ch;
}
protected String printable(String s) {
if(s == null) return null;
final int sLen = s.length();
StringBuffer buf = new StringBuffer(sLen + 10);
for(int i = 0; i < sLen; ++i) {
buf.append(printable(s.charAt(i)));
}
s = buf.toString();
return s;
}
//
// Imported code from ASF Harmony project rev 770909
// http://svn.apache.org/repos/asf/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/Character.java
//
private static int toCodePoint( char high, char low )
{
// See RFC 2781, Section 2.2
// http://www.faqs.org/rfcs/rfc2781.html
int h = ( high & 0x3FF ) << 10;
int l = low & 0x3FF;
return ( h | l ) + 0x10000;
}
private static final char MIN_HIGH_SURROGATE = '\uD800';
private static final char MAX_HIGH_SURROGATE = '\uDBFF';
private static boolean isHighSurrogate( char ch )
{
return ( MIN_HIGH_SURROGATE <= ch && MAX_HIGH_SURROGATE >= ch );
}
private static final int MIN_CODE_POINT = 0x000000;
private static final int MAX_CODE_POINT = 0x10FFFF;
private static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x10000;
private static boolean isValidCodePoint( int codePoint )
{
return ( MIN_CODE_POINT <= codePoint && MAX_CODE_POINT >= codePoint );
}
private static boolean isSupplementaryCodePoint( int codePoint )
{
return ( MIN_SUPPLEMENTARY_CODE_POINT <= codePoint && MAX_CODE_POINT >= codePoint );
}
/**
* TODO add javadoc
*
* @param codePoint
* @return
*/
public static char[] toChars( int codePoint )
{
if ( !isValidCodePoint( codePoint ) )
{
throw new IllegalArgumentException();
}
if ( isSupplementaryCodePoint( codePoint ) )
{
int cpPrime = codePoint - 0x10000;
int high = 0xD800 | ( ( cpPrime >> 10 ) & 0x3FF );
int low = 0xDC00 | ( cpPrime & 0x3FF );
return new char[] { (char) high, (char) low };
}
return new char[] { (char) codePoint };
}
}
/*
* Indiana University Extreme! Lab Software License, Version 1.2
*
* Copyright (C) 2003 The Trustees of Indiana University.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1) All redistributions of source code must retain the above
* copyright notice, the list of authors in the original source
* code, this list of conditions and the disclaimer listed in this
* license;
*
* 2) All redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the disclaimer
* listed in this license in the documentation and/or other
* materials provided with the distribution;
*
* 3) Any documentation included with all redistributions must include
* the following acknowledgement:
*
* "This product includes software developed by the Indiana
* University Extreme! Lab. For further information please visit
* http://www.extreme.indiana.edu/"
*
* Alternatively, this acknowledgment may appear in the software
* itself, and wherever such third-party acknowledgments normally
* appear.
*
* 4) The name "Indiana University" or "Indiana University
* Extreme! Lab" shall not be used to endorse or promote
* products derived from this software without prior written
* permission from Indiana University. For written permission,
* please contact http://www.extreme.indiana.edu/.
*
* 5) Products derived from this software may not use "Indiana
* University" name nor may "Indiana University" appear in their name,
* without prior written permission of the Indiana University.
*
* Indiana University provides no reassurances that the source code
* provided does not infringe the patent or any other intellectual
* property rights of any other entity. Indiana University disclaims any
* liability to any recipient for claims brought by any other entity
* based on infringement of intellectual property rights or otherwise.
*
* LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH
* NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA
* UNIVERSITY GIVES NO WARRANTIES AND MAKES NO REPRESENTATION THAT
* SOFTWARE IS FREE OF INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR
* OTHER PROPRIETARY RIGHTS. INDIANA UNIVERSITY MAKES NO WARRANTIES THAT
* SOFTWARE IS FREE FROM "BUGS", "VIRUSES", "TROJAN HORSES", "TRAP
* DOORS", "WORMS", OR OTHER HARMFUL CODE. LICENSEE ASSUMES THE ENTIRE
* RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR ASSOCIATED MATERIALS,
* AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION GENERATED USING
* SOFTWARE.
*/
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/pull/MXSerializer.java 0000664 0000000 0000000 00000121717 11660305702 0030577 0 ustar 00root root 0000000 0000000 /* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/
// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/)
package org.codehaus.plexus.util.xml.pull;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
/**
* Implementation of XmlSerializer interface from XmlPull V1 API.
* This implementation is optimized for performance and low memory footprint.
*
*
Implemented features:
* - FEATURE_NAMES_INTERNED - when enabled all returned names
* (namespaces, prefixes) will be interned and it is required that
* all names passed as arguments MUST be interned
*
- FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE
*
* Implemented properties:
* - PROPERTY_SERIALIZER_INDENTATION
*
- PROPERTY_SERIALIZER_LINE_SEPARATOR
*
*
*/
public class MXSerializer implements XmlSerializer {
protected final static String XML_URI = "http://www.w3.org/XML/1998/namespace";
protected final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
private static final boolean TRACE_SIZING = false;
protected final String FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE =
"http://xmlpull.org/v1/doc/features.html#serializer-attvalue-use-apostrophe";
protected final String FEATURE_NAMES_INTERNED =
"http://xmlpull.org/v1/doc/features.html#names-interned";
protected final String PROPERTY_SERIALIZER_INDENTATION =
"http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
protected final String PROPERTY_SERIALIZER_LINE_SEPARATOR =
"http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
protected final static String PROPERTY_LOCATION =
"http://xmlpull.org/v1/doc/properties.html#location";
// properties/features
protected boolean namesInterned;
protected boolean attributeUseApostrophe;
protected String indentationString = null; //" ";
protected String lineSeparator = "\n";
protected String location;
protected Writer out;
protected int autoDeclaredPrefixes;
protected int depth = 0;
// element stack
protected String elNamespace[] = new String[ 2 ];
protected String elName[] = new String[ elNamespace.length ];
protected int elNamespaceCount[] = new int[ elNamespace.length ];
//namespace stack
protected int namespaceEnd = 0;
protected String namespacePrefix[] = new String[ 8 ];
protected String namespaceUri[] = new String[ namespacePrefix.length ];
protected boolean finished;
protected boolean pastRoot;
protected boolean setPrefixCalled;
protected boolean startTagIncomplete;
protected boolean doIndent;
protected boolean seenTag;
protected boolean seenBracket;
protected boolean seenBracketBracket;
// buffer output if needed to write escaped String see text(String)
private static final int BUF_LEN = Runtime.getRuntime().freeMemory() > 1000000L ? 8*1024 : 256;
protected char buf[] = new char[ BUF_LEN ];
protected static final String precomputedPrefixes[];
static {
precomputedPrefixes = new String[32]; //arbitrary number ...
for (int i = 0; i < precomputedPrefixes.length; i++)
{
precomputedPrefixes[i] = ("n"+i).intern();
}
}
private boolean checkNamesInterned = false;
private void checkInterning(String name) {
if(namesInterned && name != name.intern()) {
throw new IllegalArgumentException(
"all names passed as arguments must be interned"
+"when NAMES INTERNED feature is enabled");
}
}
protected void reset() {
location = null;
out = null;
autoDeclaredPrefixes = 0;
depth = 0;
// nullify references on all levels to allow it to be GCed
for (int i = 0; i < elNamespaceCount.length; i++)
{
elName[ i ] = null;
elNamespace[ i ] = null;
elNamespaceCount[ i ] = 2;
}
namespaceEnd = 0;
//NOTE: no need to intern() as all literal strings and string-valued constant expressions
//are interned. String literals are defined in 3.10.5 of the Java Language Specification
// just checking ...
//assert "xmlns" == "xmlns".intern();
//assert XMLNS_URI == XMLNS_URI.intern();
//TODO: how to prevent from reporting this namespace?
// this is special namespace declared for consistency with XML infoset
namespacePrefix[ namespaceEnd ] = "xmlns";
namespaceUri[ namespaceEnd ] = XMLNS_URI;
++namespaceEnd;
namespacePrefix[ namespaceEnd ] = "xml";
namespaceUri[ namespaceEnd ] = XML_URI;
++namespaceEnd;
finished = false;
pastRoot = false;
setPrefixCalled = false;
startTagIncomplete = false;
//doIntent is not changed
seenTag = false;
seenBracket = false;
seenBracketBracket = false;
}
protected void ensureElementsCapacity() {
final int elStackSize = elName.length;
//assert (depth + 1) >= elName.length;
// we add at least one extra slot ...
final int newSize = (depth >= 7 ? 2 * depth : 8) + 2; // = lucky 7 + 1 //25
if(TRACE_SIZING) {
System.err.println(
getClass().getName()+" elStackSize "+elStackSize+" ==> "+newSize);
}
final boolean needsCopying = elStackSize > 0;
String[] arr = null;
// reuse arr local variable slot
arr = new String[newSize];
if(needsCopying) System.arraycopy(elName, 0, arr, 0, elStackSize);
elName = arr;
arr = new String[newSize];
if(needsCopying) System.arraycopy(elNamespace, 0, arr, 0, elStackSize);
elNamespace = arr;
final int[] iarr = new int[newSize];
if(needsCopying) {
System.arraycopy(elNamespaceCount, 0, iarr, 0, elStackSize);
} else {
// special initialization
iarr[0] = 0;
}
elNamespaceCount = iarr;
}
protected void ensureNamespacesCapacity() { //int size) {
//int namespaceSize = namespacePrefix != null ? namespacePrefix.length : 0;
//assert (namespaceEnd >= namespacePrefix.length);
//if(size >= namespaceSize) {
//int newSize = size > 7 ? 2 * size : 8; // = lucky 7 + 1 //25
final int newSize = namespaceEnd > 7 ? 2 * namespaceEnd : 8;
if(TRACE_SIZING) {
System.err.println(
getClass().getName()+" namespaceSize "+namespacePrefix.length+" ==> "+newSize);
}
final String[] newNamespacePrefix = new String[newSize];
final String[] newNamespaceUri = new String[newSize];
if(namespacePrefix != null) {
System.arraycopy(
namespacePrefix, 0, newNamespacePrefix, 0, namespaceEnd);
System.arraycopy(
namespaceUri, 0, newNamespaceUri, 0, namespaceEnd);
}
namespacePrefix = newNamespacePrefix;
namespaceUri = newNamespaceUri;
// TODO use hashes for quick namespace->prefix lookups
// if( ! allStringsInterned ) {
// int[] newNamespacePrefixHash = new int[newSize];
// if(namespacePrefixHash != null) {
// System.arraycopy(
// namespacePrefixHash, 0, newNamespacePrefixHash, 0, namespaceEnd);
// }
// namespacePrefixHash = newNamespacePrefixHash;
// }
//prefixesSize = newSize;
// ////assert nsPrefixes.length > size && nsPrefixes.length == newSize
//}
}
public void setFeature(String name,
boolean state) throws IllegalArgumentException, IllegalStateException
{
if(name == null) {
throw new IllegalArgumentException("feature name can not be null");
}
if(FEATURE_NAMES_INTERNED.equals(name)) {
namesInterned = state;
} else if(FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) {
attributeUseApostrophe = state;
} else {
throw new IllegalStateException("unsupported feature "+name);
}
}
public boolean getFeature(String name) throws IllegalArgumentException
{
if(name == null) {
throw new IllegalArgumentException("feature name can not be null");
}
if(FEATURE_NAMES_INTERNED.equals(name)) {
return namesInterned;
} else if(FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) {
return attributeUseApostrophe;
} else {
return false;
}
}
// precomputed variables to simplify writing indentation
protected int offsetNewLine;
protected int indentationJump;
protected char[] indentationBuf;
protected int maxIndentLevel;
protected boolean writeLineSepartor; //should end-of-line be written
protected boolean writeIndentation; // is indentation used?
/**
* For maximum efficiency when writing indents the required output is pre-computed
* This is internal function that recomputes buffer after user requested chnages.
*/
protected void rebuildIndentationBuf() {
if(doIndent == false) return;
final int maxIndent = 65; //hardcoded maximum indentation size in characters
int bufSize = 0;
offsetNewLine = 0;
if(writeLineSepartor) {
offsetNewLine = lineSeparator.length();
bufSize += offsetNewLine;
}
maxIndentLevel = 0;
if(writeIndentation) {
indentationJump = indentationString.length();
maxIndentLevel = maxIndent / indentationJump;
bufSize += maxIndentLevel * indentationJump;
}
if(indentationBuf == null || indentationBuf.length < bufSize) {
indentationBuf = new char[bufSize + 8];
}
int bufPos = 0;
if(writeLineSepartor) {
for (int i = 0; i < lineSeparator.length(); i++)
{
indentationBuf[ bufPos++ ] = lineSeparator.charAt(i);
}
}
if(writeIndentation) {
for (int i = 0; i < maxIndentLevel; i++)
{
for (int j = 0; j < indentationString.length(); j++)
{
indentationBuf[ bufPos++ ] = indentationString.charAt(j);
}
}
}
}
// if(doIndent) writeIndent();
protected void writeIndent() throws IOException {
final int start = writeLineSepartor ? 0 : offsetNewLine;
final int level = (depth > maxIndentLevel) ? maxIndentLevel : depth;
out.write( indentationBuf, start, (level * indentationJump) + offsetNewLine);
}
public void setProperty(String name,
Object value) throws IllegalArgumentException, IllegalStateException
{
if(name == null) {
throw new IllegalArgumentException("property name can not be null");
}
if(PROPERTY_SERIALIZER_INDENTATION.equals(name)) {
indentationString = (String)value;
} else if(PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) {
lineSeparator = (String)value;
} else if(PROPERTY_LOCATION.equals(name)) {
location = (String) value;
} else {
throw new IllegalStateException("unsupported property "+name);
}
writeLineSepartor = lineSeparator != null && lineSeparator.length() > 0;
writeIndentation = indentationString != null && indentationString.length() > 0;
// optimize - do not write when nothing to write ...
doIndent = indentationString != null && (writeLineSepartor || writeIndentation);
//NOTE: when indentationString == null there is no indentation
// (even though writeLineSeparator may be true ...)
rebuildIndentationBuf();
seenTag = false; // for consistency
}
public Object getProperty(String name) throws IllegalArgumentException
{
if(name == null) {
throw new IllegalArgumentException("property name can not be null");
}
if(PROPERTY_SERIALIZER_INDENTATION.equals(name)) {
return indentationString;
} else if(PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) {
return lineSeparator;
} else if(PROPERTY_LOCATION.equals(name)) {
return location;
} else {
return null;
}
}
private String getLocation() {
return location != null ? " @"+location : "";
}
// this is special method that can be accessed directly to retrieve Writer serializer is using
public Writer getWriter()
{
return out;
}
public void setOutput(Writer writer)
{
reset();
out = writer;
}
public void setOutput(OutputStream os, String encoding) throws IOException
{
if(os == null) throw new IllegalArgumentException("output stream can not be null");
reset();
if(encoding != null) {
out = new OutputStreamWriter(os, encoding);
} else {
out = new OutputStreamWriter(os);
}
}
public void startDocument (String encoding, Boolean standalone) throws IOException
{
char apos = attributeUseApostrophe ? '\'' : '"';
if(attributeUseApostrophe) {
out.write("");
if(writeLineSepartor) {
out.write(lineSeparator);
}
}
public void endDocument() throws IOException
{
// close all unclosed tag;
while(depth > 0) {
endTag(elNamespace[ depth ], elName[ depth ]);
}
if(writeLineSepartor) {
out.write(lineSeparator);
}
//assert depth == 0;
//assert startTagIncomplete == false;
finished = pastRoot = startTagIncomplete = true;
out.flush();
}
public void setPrefix(String prefix, String namespace) throws IOException
{
if(startTagIncomplete) closeStartTag();
//assert prefix != null;
//assert namespace != null;
if (prefix == null) {
prefix = "";
}
if(!namesInterned) {
prefix = prefix.intern(); //will throw NPE if prefix==null
} else if(checkNamesInterned) {
checkInterning(prefix);
} else if(prefix == null) {
throw new IllegalArgumentException("prefix must be not null"+getLocation());
}
//check that prefix is not duplicated ...
for (int i = elNamespaceCount[ depth ]; i < namespaceEnd; i++)
{
if(prefix == namespacePrefix[ i ]) {
throw new IllegalStateException("duplicated prefix "+printable(prefix)+getLocation());
}
}
if(!namesInterned) {
namespace = namespace.intern();
} else if(checkNamesInterned) {
checkInterning(namespace);
} else if(namespace == null) {
throw new IllegalArgumentException("namespace must be not null"+getLocation());
}
if(namespaceEnd >= namespacePrefix.length) {
ensureNamespacesCapacity();
}
namespacePrefix[ namespaceEnd ] = prefix;
namespaceUri[ namespaceEnd ] = namespace;
++namespaceEnd;
setPrefixCalled = true;
}
protected String lookupOrDeclarePrefix( String namespace ) {
return getPrefix(namespace, true);
}
public String getPrefix(String namespace, boolean generatePrefix)
{
//assert namespace != null;
if(!namesInterned) {
// when String is interned we can do much faster namespace stack lookups ...
namespace = namespace.intern();
} else if(checkNamesInterned) {
checkInterning(namespace);
//assert namespace != namespace.intern();
}
if(namespace == null) {
throw new IllegalArgumentException("namespace must be not null"+getLocation());
} else if(namespace.length() == 0) {
throw new IllegalArgumentException("default namespace cannot have prefix"+getLocation());
}
// first check if namespace is already in scope
for (int i = namespaceEnd - 1; i >= 0 ; --i)
{
if(namespace == namespaceUri[ i ]) {
final String prefix = namespacePrefix[ i ];
// now check that prefix is still in scope
for (int p = namespaceEnd - 1; p > i ; --p)
{
if(prefix == namespacePrefix[ p ])
continue; // too bad - prefix is redeclared with different namespace
}
return prefix;
}
}
// so not found it ...
if(!generatePrefix) {
return null;
}
return generatePrefix(namespace);
}
private String generatePrefix(String namespace) {
//assert namespace == namespace.intern();
while(true) {
++autoDeclaredPrefixes;
//fast lookup uses table that was pre-initialized in static{} ....
final String prefix = autoDeclaredPrefixes < precomputedPrefixes.length
? precomputedPrefixes[autoDeclaredPrefixes] : ("n"+autoDeclaredPrefixes).intern();
// make sure this prefix is not declared in any scope (avoid hiding in-scope prefixes)!
for (int i = namespaceEnd - 1; i >= 0 ; --i)
{
if(prefix == namespacePrefix[ i ]) {
continue; // prefix is already declared - generate new and try again
}
}
// declare prefix
if(namespaceEnd >= namespacePrefix.length) {
ensureNamespacesCapacity();
}
namespacePrefix[ namespaceEnd ] = prefix;
namespaceUri[ namespaceEnd ] = namespace;
++namespaceEnd;
return prefix;
}
}
public int getDepth()
{
return depth;
}
public String getNamespace ()
{
return elNamespace[depth];
}
public String getName()
{
return elName[depth];
}
public XmlSerializer startTag (String namespace, String name) throws IOException
{
if(startTagIncomplete) {
closeStartTag();
}
seenBracket = seenBracketBracket = false;
if(doIndent && depth > 0 && seenTag) {
writeIndent();
}
seenTag = true;
setPrefixCalled = false;
startTagIncomplete = true;
++depth;
if( (depth + 1) >= elName.length) {
ensureElementsCapacity();
}
////assert namespace != null;
if(checkNamesInterned && namesInterned) checkInterning(namespace);
elNamespace[ depth ] = (namesInterned || namespace == null) ? namespace : namespace.intern();
//assert name != null;
//elName[ depth ] = name;
if(checkNamesInterned && namesInterned) checkInterning(name);
elName[ depth ] = (namesInterned || name == null) ? name : name.intern();
if(out == null) {
throw new IllegalStateException("setOutput() must called set before serialization can start");
}
out.write('<');
if(namespace != null) {
if(namespace.length() > 0) {
//ALEK: in future make it as feature on serializer
String prefix = null;
if(depth > 0 && (namespaceEnd - elNamespaceCount[depth-1]) == 1) {
// if only one prefix was declared un-declare it if prefix is already declared on parent el with the same URI
String uri = namespaceUri[namespaceEnd-1];
if(uri == namespace || uri.equals(namespace)) {
String elPfx = namespacePrefix[namespaceEnd-1];
// 2 == to skip predefined namespaces (xml and xmlns ...)
for(int pos = elNamespaceCount[depth-1] - 1; pos >= 2; --pos ) {
String pf = namespacePrefix[pos];
if(pf == elPfx || pf.equals(elPfx)) {
String n = namespaceUri[pos];
if(n == uri || n.equals(uri)) {
--namespaceEnd; //un-declare namespace
prefix = elPfx;
}
break;
}
}
}
}
if(prefix == null) {
prefix = lookupOrDeclarePrefix( namespace );
}
//assert prefix != null;
// make sure that default ("") namespace to not print ":"
if(prefix.length() > 0) {
out.write(prefix);
out.write(':');
}
} else {
// make sure that default namespace can be declared
for (int i = namespaceEnd - 1; i >= 0 ; --i)
{
if(namespacePrefix[ i ] == "") {
final String uri = namespaceUri[ i ];
if(uri == null) {
// declare default namespace
setPrefix("", "");
} else if(uri.length() > 0) {
throw new IllegalStateException(
"start tag can not be written in empty default namespace "+
"as default namespace is currently bound to '"+uri+"'"+getLocation());
}
break;
}
}
}
}
out.write(name);
return this;
}
public XmlSerializer attribute (String namespace, String name,
String value) throws IOException
{
if(!startTagIncomplete) {
throw new IllegalArgumentException("startTag() must be called before attribute()"+getLocation());
}
//assert setPrefixCalled == false;
out.write(' ');
////assert namespace != null;
if(namespace != null && namespace.length() > 0) {
//namespace = namespace.intern();
if(!namesInterned) {
namespace = namespace.intern();
} else if(checkNamesInterned) {
checkInterning(namespace);
}
String prefix = lookupOrDeclarePrefix( namespace );
//assert( prefix != null);
if(prefix.length() == 0) {
// needs to declare prefix to hold default namespace
//NOTE: attributes such as a='b' are in NO namespace
prefix = generatePrefix(namespace);
}
out.write(prefix);
out.write(':');
// if(prefix.length() > 0) {
// out.write(prefix);
// out.write(':');
// }
}
//assert name != null;
out.write(name);
out.write('=');
//assert value != null;
out.write( attributeUseApostrophe ? '\'' : '"');
writeAttributeValue(value, out);
out.write( attributeUseApostrophe ? '\'' : '"');
return this;
}
protected void closeStartTag() throws IOException {
if(finished) {
throw new IllegalArgumentException("trying to write past already finished output"+getLocation());
}
if(seenBracket) {
seenBracket = seenBracketBracket = false;
}
if( startTagIncomplete || setPrefixCalled ) {
if(setPrefixCalled) {
throw new IllegalArgumentException(
"startTag() must be called immediately after setPrefix()"+getLocation());
}
if(!startTagIncomplete) {
throw new IllegalArgumentException("trying to close start tag that is not opened"+getLocation());
}
// write all namespace declarations!
writeNamespaceDeclarations();
out.write('>');
elNamespaceCount[ depth ] = namespaceEnd;
startTagIncomplete = false;
}
}
private void writeNamespaceDeclarations() throws IOException
{
//int start = elNamespaceCount[ depth - 1 ];
for (int i = elNamespaceCount[ depth - 1 ]; i < namespaceEnd; i++)
{
if(doIndent && namespaceUri[ i ].length() > 40) {
writeIndent();
out.write(" ");
}
if(namespacePrefix[ i ] != "") {
out.write(" xmlns:");
out.write(namespacePrefix[ i ]);
out.write('=');
} else {
out.write(" xmlns=");
}
out.write( attributeUseApostrophe ? '\'' : '"');
//NOTE: escaping of namespace value the same way as attributes!!!!
writeAttributeValue(namespaceUri[ i ], out);
out.write( attributeUseApostrophe ? '\'' : '"');
}
}
public XmlSerializer endTag(String namespace, String name) throws IOException
{
// check that level is valid
////assert namespace != null;
//if(namespace != null) {
// namespace = namespace.intern();
//}
seenBracket = seenBracketBracket = false;
if(namespace != null) {
if(!namesInterned) {
namespace = namespace.intern();
} else if(checkNamesInterned) {
checkInterning(namespace);
}
}
if(namespace != elNamespace[ depth ])
{
throw new IllegalArgumentException(
"expected namespace "+printable(elNamespace[ depth ])
+" and not "+printable(namespace)+getLocation());
}
if(name == null) {
throw new IllegalArgumentException("end tag name can not be null"+getLocation());
}
if(checkNamesInterned && namesInterned) {
checkInterning(name);
}
if((!namesInterned && !name.equals(elName[ depth ]))
|| (namesInterned && name != elName[ depth ]))
{
throw new IllegalArgumentException(
"expected element name "+printable(elName[ depth ])+" and not "+printable(name)+getLocation());
}
if(startTagIncomplete) {
writeNamespaceDeclarations();
out.write(" />"); //space is added to make it easier to work in XHTML!!!
--depth;
} else {
--depth;
//assert startTagIncomplete == false;
if(doIndent && seenTag) { writeIndent(); }
out.write("");
if(namespace != null && namespace.length() > 0) {
//TODO prefix should be alredy known from matching start tag ...
final String prefix = lookupOrDeclarePrefix( namespace );
//assert( prefix != null);
if(prefix.length() > 0) {
out.write(prefix);
out.write(':');
}
}
out.write(name);
out.write('>');
}
namespaceEnd = elNamespaceCount[ depth ];
startTagIncomplete = false;
seenTag = true;
return this;
}
public XmlSerializer text (String text) throws IOException
{
//assert text != null;
if(startTagIncomplete || setPrefixCalled) closeStartTag();
if(doIndent && seenTag) seenTag = false;
writeElementContent(text, out);
return this;
}
public XmlSerializer text (char [] buf, int start, int len) throws IOException
{
if(startTagIncomplete || setPrefixCalled) closeStartTag();
if(doIndent && seenTag) seenTag = false;
writeElementContent(buf, start, len, out);
return this;
}
public void cdsect (String text) throws IOException
{
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
if(doIndent && seenTag) seenTag = false;
out.write("");
}
public void entityRef (String text) throws IOException
{
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
if(doIndent && seenTag) seenTag = false;
out.write('&');
out.write(text); //escape?
out.write(';');
}
public void processingInstruction (String text) throws IOException
{
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
if(doIndent && seenTag) seenTag = false;
out.write("");
out.write(text); //escape?
out.write("?>");
}
public void comment (String text) throws IOException
{
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
if(doIndent && seenTag) seenTag = false;
out.write("");
}
public void docdecl (String text) throws IOException
{
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
if(doIndent && seenTag) seenTag = false;
out.write("");
}
public void ignorableWhitespace (String text) throws IOException
{
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
if(doIndent && seenTag) seenTag = false;
if(text.length() == 0) {
throw new IllegalArgumentException(
"empty string is not allowed for ignorable whitespace"+getLocation());
}
out.write(text); //no escape?
}
public void flush () throws IOException
{
if(!finished && startTagIncomplete) closeStartTag();
out.flush();
}
// --- utility methods
protected void writeAttributeValue(String value, Writer out) throws IOException
{
//.[apostrophe and <, & escaped],
final char quot = attributeUseApostrophe ? '\'' : '"';
final String quotEntity = attributeUseApostrophe ? "'" : """;
int pos = 0;
for (int i = 0; i < value.length(); i++)
{
char ch = value.charAt(i);
if(ch == '&') {
if(i > pos) out.write(value.substring(pos, i));
out.write("&");
pos = i + 1;
} if(ch == '<') {
if(i > pos) out.write(value.substring(pos, i));
out.write("<");
pos = i + 1;
}else if(ch == quot) {
if(i > pos) out.write(value.substring(pos, i));
out.write(quotEntity);
pos = i + 1;
} else if(ch < 32) {
//in XML 1.0 only legal character are #x9 | #xA | #xD
// and they must be escaped otherwise in attribute value they are normalized to spaces
if(ch == 13 || ch == 10 || ch == 9) {
if(i > pos) out.write(value.substring(pos, i));
out.write("");
out.write(Integer.toString(ch));
out.write(';');
pos = i + 1;
} else {
throw new IllegalStateException(
"character "+Integer.toString(ch)+" is not allowed in output"+getLocation());
// in XML 1.1 legal are [#x1-#xD7FF]
// if(ch > 0) {
// if(i > pos) out.write(text.substring(pos, i));
// out.write("");
// out.write(Integer.toString(ch));
// out.write(';');
// pos = i + 1;
// } else {
// throw new IllegalStateException(
// "character zero is not allowed in XML 1.1 output"+getLocation());
// }
}
}
}
if(pos > 0) {
out.write(value.substring(pos));
} else {
out.write(value); // this is shortcut to the most common case
}
}
protected void writeElementContent(String text, Writer out) throws IOException
{
// escape '<', '&', ']]>', <32 if necessary
int pos = 0;
for (int i = 0; i < text.length(); i++)
{
//TODO: check if doing char[] text.getChars() would be faster than getCharAt(i) ...
char ch = text.charAt(i);
if(ch == ']') {
if(seenBracket) {
seenBracketBracket = true;
} else {
seenBracket = true;
}
} else {
if(ch == '&') {
if(i > pos) out.write(text.substring(pos, i));
out.write("&");
pos = i + 1;
} else if(ch == '<') {
if(i > pos) out.write(text.substring(pos, i));
out.write("<");
pos = i + 1;
} else if(seenBracketBracket && ch == '>') {
if(i > pos) out.write(text.substring(pos, i));
out.write(">");
pos = i + 1;
} else if(ch < 32) {
//in XML 1.0 only legal character are #x9 | #xA | #xD
if( ch == 9 || ch == 10 || ch == 13) {
// pass through
// } else if(ch == 13) { //escape
// if(i > pos) out.write(text.substring(pos, i));
// out.write("");
// out.write(Integer.toString(ch));
// out.write(';');
// pos = i + 1;
} else {
throw new IllegalStateException(
"character "+Integer.toString(ch)+" is not allowed in output"+getLocation());
// in XML 1.1 legal are [#x1-#xD7FF]
// if(ch > 0) {
// if(i > pos) out.write(text.substring(pos, i));
// out.write("");
// out.write(Integer.toString(ch));
// out.write(';');
// pos = i + 1;
// } else {
// throw new IllegalStateException(
// "character zero is not allowed in XML 1.1 output"+getLocation());
// }
}
}
if(seenBracket) {
seenBracketBracket = seenBracket = false;
}
}
}
if(pos > 0) {
out.write(text.substring(pos));
} else {
out.write(text); // this is shortcut to the most common case
}
}
protected void writeElementContent(char[] buf, int off, int len, Writer out) throws IOException
{
// escape '<', '&', ']]>'
final int end = off + len;
int pos = off;
for (int i = off; i < end; i++)
{
final char ch = buf[i];
if(ch == ']') {
if(seenBracket) {
seenBracketBracket = true;
} else {
seenBracket = true;
}
} else {
if(ch == '&') {
if(i > pos) {
out.write(buf, pos, i - pos);
}
out.write("&");
pos = i + 1;
} else if(ch == '<') {
if(i > pos) {
out.write(buf, pos, i - pos);
}
out.write("<");
pos = i + 1;
} else if(seenBracketBracket && ch == '>') {
if(i > pos) {
out.write(buf, pos, i - pos);
}
out.write(">");
pos = i + 1;
} else if(ch < 32) {
//in XML 1.0 only legal character are #x9 | #xA | #xD
if( ch == 9 || ch == 10 || ch == 13) {
// pass through
// } else if(ch == 13 ) { //if(ch == '\r') {
// if(i > pos) {
// out.write(buf, pos, i - pos);
// }
// out.write("");
// out.write(Integer.toString(ch));
// out.write(';');
// pos = i + 1;
} else {
throw new IllegalStateException(
"character "+Integer.toString(ch)+" is not allowed in output"+getLocation());
// in XML 1.1 legal are [#x1-#xD7FF]
// if(ch > 0) {
// if(i > pos) out.write(text.substring(pos, i));
// out.write("");
// out.write(Integer.toString(ch));
// out.write(';');
// pos = i + 1;
// } else {
// throw new IllegalStateException(
// "character zero is not allowed in XML 1.1 output"+getLocation());
// }
}
}
if(seenBracket) {
seenBracketBracket = seenBracket = false;
}
// assert seenBracketBracket == seenBracket == false;
}
}
if(end > pos) {
out.write(buf, pos, end - pos);
}
}
/** simple utility method -- good for debugging */
protected static final String printable(String s) {
if(s == null) return "null";
StringBuffer retval = new StringBuffer(s.length() + 16);
retval.append("'");
char ch;
for (int i = 0; i < s.length(); i++) {
addPrintable(retval, s.charAt(i));
}
retval.append("'");
return retval.toString();
}
protected static final String printable(char ch) {
StringBuffer retval = new StringBuffer();
addPrintable(retval, ch);
return retval.toString();
}
private static void addPrintable(StringBuffer retval, char ch)
{
switch (ch)
{
case '\b':
retval.append("\\b");
break;
case '\t':
retval.append("\\t");
break;
case '\n':
retval.append("\\n");
break;
case '\f':
retval.append("\\f");
break;
case '\r':
retval.append("\\r");
break;
case '\"':
retval.append("\\\"");
break;
case '\'':
retval.append("\\\'");
break;
case '\\':
retval.append("\\\\");
break;
default:
if (ch < 0x20 || ch > 0x7e) {
final String ss = "0000" + Integer.toString(ch, 16);
retval.append("\\u" + ss.substring(ss.length() - 4, ss.length()));
} else {
retval.append(ch);
}
}
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/pull/XmlPullParser.java 0000664 0000000 0000000 00000127730 11660305702 0030774 0 ustar 00root root 0000000 0000000 /* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/
// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/)
package org.codehaus.plexus.util.xml.pull;
import java.io.InputStream;
import java.io.IOException;
import java.io.Reader;
/**
* XML Pull Parser is an interface that defines parsing functionality provided
* in XMLPULL V1 API (visit this website to
* learn more about API and its implementations).
*
* There are following different
* kinds of parser depending on which features are set:
* - non-validating parser as defined in XML 1.0 spec when
* FEATURE_PROCESS_DOCDECL is set to true
*
- validating parser as defined in XML 1.0 spec when
* FEATURE_VALIDATION is true (and that implies that FEATURE_PROCESS_DOCDECL is true)
*
- when FEATURE_PROCESS_DOCDECL is false (this is default and
* if different value is required necessary must be changed before parsing is started)
* then parser behaves like XML 1.0 compliant non-validating parser under condition that
* no DOCDECL is present in XML documents
* (internal entities can still be defined with defineEntityReplacementText()).
* This mode of operation is intended for operation in constrained environments such as J2ME.
*
*
*
* There are two key methods: next() and nextToken(). While next() provides
* access to high level parsing events, nextToken() allows access to lower
* level tokens.
*
*
The current event state of the parser
* can be determined by calling the
* getEventType() method.
* Initially, the parser is in the START_DOCUMENT
* state.
*
*
The method next() advances the parser to the
* next event. The int value returned from next determines the current parser
* state and is identical to the value returned from following calls to
* getEventType ().
*
*
The following event types are seen by next()
* - START_TAG
- An XML start tag was read.
*
- TEXT
- Text content was read;
* the text content can be retrieved using the getText() method.
* (when in validating mode next() will not report ignorable whitespaces, use nextToken() instead)
*
- END_TAG
- An end tag was read
*
- END_DOCUMENT
- No more events are available
*
*
* after first next() or nextToken() (or any other next*() method)
* is called user application can obtain
* XML version, standalone and encoding from XML declaration
* in following ways:
* - version:
* getProperty("http://xmlpull.org/v1/doc/properties.html#xmldecl-version")
* returns String ("1.0") or null if XMLDecl was not read or if property is not supported
*
- standalone:
* getProperty("http://xmlpull.org/v1/doc/features.html#xmldecl-standalone")
* returns Boolean: null if there was no standalone declaration
* or if property is not supported
* otherwise returns Boolean(true) if standalone="yes" and Boolean(false) when standalone="no"
*
- encoding: obtained from getInputEncoding()
* null if stream had unknown encoding (not set in setInputStream)
* and it was not declared in XMLDecl
*
*
* A minimal example for using this API may look as follows:
*
* import java.io.IOException;
* import java.io.StringReader;
*
* import org.xmlpull.v1.XmlPullParser;
* import org.xmlpull.v1.XmlPullParserException.html;
* import org.xmlpull.v1.XmlPullParserFactory;
*
* public class SimpleXmlPullApp
* {
*
* public static void main (String args[])
* throws XmlPullParserException, IOException
* {
* XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
* factory.setNamespaceAware(true);
* XmlPullParser xpp = factory.newPullParser();
*
* xpp.setInput( new StringReader ( "<foo>Hello World!</foo>" ) );
* int eventType = xpp.getEventType();
* while (eventType != xpp.END_DOCUMENT) {
* if(eventType == xpp.START_DOCUMENT) {
* System.out.println("Start document");
* } else if(eventType == xpp.END_DOCUMENT) {
* System.out.println("End document");
* } else if(eventType == xpp.START_TAG) {
* System.out.println("Start tag "+xpp.getName());
* } else if(eventType == xpp.END_TAG) {
* System.out.println("End tag "+xpp.getName());
* } else if(eventType == xpp.TEXT) {
* System.out.println("Text "+xpp.getText());
* }
* eventType = xpp.next();
* }
* }
* }
*
*
* The above example will generate the following output:
*
* Start document
* Start tag foo
* Text Hello World!
* End tag foo
*
*
* For more details on API usage, please refer to the
* quick Introduction available at http://www.xmlpull.org
*
* @see #defineEntityReplacementText
* @see #getName
* @see #getNamespace
* @see #getText
* @see #next
* @see #nextToken
* @see #setInput
* @see #FEATURE_PROCESS_DOCDECL
* @see #FEATURE_VALIDATION
* @see #START_DOCUMENT
* @see #START_TAG
* @see #TEXT
* @see #END_TAG
* @see #END_DOCUMENT
*
* @author Stefan Haustein
* @author Aleksander Slominski
*/
public interface XmlPullParser {
/** This constant represents the default namespace (empty string "") */
String NO_NAMESPACE = "";
// ----------------------------------------------------------------------------
// EVENT TYPES as reported by next()
/**
* Signalize that parser is at the very beginning of the document
* and nothing was read yet.
* This event type can only be observed by calling getEvent()
* before the first call to next(), nextToken, or nextTag()).
*
* @see #next
* @see #nextToken
*/
int START_DOCUMENT = 0;
/**
* Logical end of the xml document. Returned from getEventType, next()
* and nextToken()
* when the end of the input document has been reached.
*
NOTE: calling again
* next() or nextToken()
* will result in exception being thrown.
*
* @see #next
* @see #nextToken
*/
int END_DOCUMENT = 1;
/**
* Returned from getEventType(),
* next(), nextToken() when
* a start tag was read.
* The name of start tag is available from getName(), its namespace and prefix are
* available from getNamespace() and getPrefix()
* if namespaces are enabled.
* See getAttribute* methods to retrieve element attributes.
* See getNamespace* methods to retrieve newly declared namespaces.
*
* @see #next
* @see #nextToken
* @see #getName
* @see #getPrefix
* @see #getNamespace
* @see #getAttributeCount
* @see #getDepth
* @see #getNamespaceCount
* @see #getNamespace
* @see #FEATURE_PROCESS_NAMESPACES
*/
int START_TAG = 2;
/**
* Returned from getEventType(), next(), or
* nextToken() when an end tag was read.
* The name of start tag is available from getName(), its
* namespace and prefix are
* available from getNamespace() and getPrefix().
*
* @see #next
* @see #nextToken
* @see #getName
* @see #getPrefix
* @see #getNamespace
* @see #FEATURE_PROCESS_NAMESPACES
*/
int END_TAG = 3;
/**
* Character data was read and will is available by calling getText().
*
Please note: next() will
* accumulate multiple
* events into one TEXT event, skipping IGNORABLE_WHITESPACE,
* PROCESSING_INSTRUCTION and COMMENT events,
* In contrast, nextToken() will stop reading
* text when any other event is observed.
* Also, when the state was reached by calling next(), the text value will
* be normalized, whereas getText() will
* return unnormalized content in the case of nextToken(). This allows
* an exact roundtrip without changing line ends when examining low
* level events, whereas for high level applications the text is
* normalized appropriately.
*
* @see #next
* @see #nextToken
* @see #getText
*/
int TEXT = 4;
// ----------------------------------------------------------------------------
// additional events exposed by lower level nextToken()
/**
* A CDATA sections was just read;
* this token is available only from calls to nextToken().
* A call to next() will accumulate various text events into a single event
* of type TEXT. The text contained in the CDATA section is available
* by calling getText().
*
* @see #nextToken
* @see #getText
*/
int CDSECT = 5;
/**
* An entity reference was just read;
* this token is available from nextToken()
* only. The entity name is available by calling getName(). If available,
* the replacement text can be obtained by calling getTextt(); otherwise,
* the user is responsible for resolving the entity reference.
* This event type is never returned from next(); next() will
* accumulate the replacement text and other text
* events to a single TEXT event.
*
* @see #nextToken
* @see #getText
*/
int ENTITY_REF = 6;
/**
* Ignorable whitespace was just read.
* This token is available only from nextToken()).
* For non-validating
* parsers, this event is only reported by nextToken() when outside
* the root element.
* Validating parsers may be able to detect ignorable whitespace at
* other locations.
* The ignorable whitespace string is available by calling getText()
*
*
NOTE: this is different from calling the
* isWhitespace() method, since text content
* may be whitespace but not ignorable.
*
* Ignorable whitespace is skipped by next() automatically; this event
* type is never returned from next().
*
* @see #nextToken
* @see #getText
*/
int IGNORABLE_WHITESPACE = 7;
/**
* An XML processing instruction declaration was just read. This
* event type is available only via nextToken().
* getText() will return text that is inside the processing instruction.
* Calls to next() will skip processing instructions automatically.
* @see #nextToken
* @see #getText
*/
int PROCESSING_INSTRUCTION = 8;
/**
* An XML comment was just read. This event type is this token is
* available via nextToken() only;
* calls to next() will skip comments automatically.
* The content of the comment can be accessed using the getText()
* method.
*
* @see #nextToken
* @see #getText
*/
int COMMENT = 9;
/**
* An XML document type declaration was just read. This token is
* available from nextToken() only.
* The unparsed text inside the doctype is available via
* the getText() method.
*
* @see #nextToken
* @see #getText
*/
int DOCDECL = 10;
/**
* This array can be used to convert the event type integer constants
* such as START_TAG or TEXT to
* to a string. For example, the value of TYPES[START_TAG] is
* the string "START_TAG".
*
* This array is intended for diagnostic output only. Relying
* on the contents of the array may be dangerous since malicious
* applications may alter the array, although it is final, due
* to limitations of the Java language.
*/
String [] TYPES = {
"START_DOCUMENT",
"END_DOCUMENT",
"START_TAG",
"END_TAG",
"TEXT",
"CDSECT",
"ENTITY_REF",
"IGNORABLE_WHITESPACE",
"PROCESSING_INSTRUCTION",
"COMMENT",
"DOCDECL"
};
// ----------------------------------------------------------------------------
// namespace related features
/**
* This feature determines whether the parser processes
* namespaces. As for all features, the default value is false.
*
NOTE: The value can not be changed during
* parsing an must be set before parsing.
*
* @see #getFeature
* @see #setFeature
*/
String FEATURE_PROCESS_NAMESPACES =
"http://xmlpull.org/v1/doc/features.html#process-namespaces";
/**
* This feature determines whether namespace attributes are
* exposed via the attribute access methods. Like all features,
* the default value is false. This feature cannot be changed
* during parsing.
*
* @see #getFeature
* @see #setFeature
*/
String FEATURE_REPORT_NAMESPACE_ATTRIBUTES =
"http://xmlpull.org/v1/doc/features.html#report-namespace-prefixes";
/**
* This feature determines whether the document declaration
* is processed. If set to false,
* the DOCDECL event type is reported by nextToken()
* and ignored by next().
*
* If this featue is activated, then the document declaration
* must be processed by the parser.
*
*
Please note: If the document type declaration
* was ignored, entity references may cause exceptions
* later in the parsing process.
* The default value of this feature is false. It cannot be changed
* during parsing.
*
* @see #getFeature
* @see #setFeature
*/
String FEATURE_PROCESS_DOCDECL =
"http://xmlpull.org/v1/doc/features.html#process-docdecl";
/**
* If this feature is activated, all validation errors as
* defined in the XML 1.0 sepcification are reported.
* This implies that FEATURE_PROCESS_DOCDECL is true and both, the
* internal and external document type declaration will be processed.
*
Please Note: This feature can not be changed
* during parsing. The default value is false.
*
* @see #getFeature
* @see #setFeature
*/
String FEATURE_VALIDATION =
"http://xmlpull.org/v1/doc/features.html#validation";
/**
* Use this call to change the general behaviour of the parser,
* such as namespace processing or doctype declaration handling.
* This method must be called before the first call to next or
* nextToken. Otherwise, an exception is thrown.
*
Example: call setFeature(FEATURE_PROCESS_NAMESPACES, true) in order
* to switch on namespace processing. The initial settings correspond
* to the properties requested from the XML Pull Parser factory.
* If none were requested, all feautures are deactivated by default.
*
* @exception XmlPullParserException If the feature is not supported or can not be set
* @exception IllegalArgumentException If string with the feature name is null
*/
void setFeature(String name,
boolean state) throws XmlPullParserException;
/**
* Returns the current value of the given feature.
*
Please note: unknown features are
* always returned as false.
*
* @param name The name of feature to be retrieved.
* @return The value of the feature.
* @exception IllegalArgumentException if string the feature name is null
*/
boolean getFeature(String name);
/**
* Set the value of a property.
*
* The property name is any fully-qualified URI.
*
* @exception XmlPullParserException If the property is not supported or can not be set
* @exception IllegalArgumentException If string with the property name is null
*/
void setProperty(String name,
Object value) throws XmlPullParserException;
/**
* Look up the value of a property.
*
* The property name is any fully-qualified URI.
*
NOTE: unknown properties are always
* returned as null.
*
* @param name The name of property to be retrieved.
* @return The value of named property.
*/
Object getProperty(String name);
/**
* Set the input source for parser to the given reader and
* resets the parser. The event type is set to the initial value
* START_DOCUMENT.
* Setting the reader to null will just stop parsing and
* reset parser state,
* allowing the parser to free internal resources
* such as parsing buffers.
*/
void setInput(Reader in) throws XmlPullParserException;
/**
* Sets the input stream the parser is going to process.
* This call resets the parser state and sets the event type
* to the initial value START_DOCUMENT.
*
*
NOTE: If an input encoding string is passed,
* it MUST be used. Otherwise,
* if inputEncoding is null, the parser SHOULD try to determine
* input encoding following XML 1.0 specification (see below).
* If encoding detection is supported then following feature
* http://xmlpull.org/v1/doc/features.html#detect-encoding
* MUST be true amd otherwise it must be false
*
* @param inputStream contains a raw byte input stream of possibly
* unknown encoding (when inputEncoding is null).
*
* @param inputEncoding if not null it MUST be used as encoding for inputStream
*/
void setInput(InputStream inputStream, String inputEncoding)
throws XmlPullParserException;
/**
* Returns the input encoding if known, null otherwise.
* If setInput(InputStream, inputEncoding) was called with an inputEncoding
* value other than null, this value must be returned
* from this method. Otherwise, if inputEncoding is null and
* the parser suppports the encoding detection feature
* (http://xmlpull.org/v1/doc/features.html#detect-encoding),
* it must return the detected encoding.
* If setInput(Reader) was called, null is returned.
* After first call to next if XML declaration was present this method
* will return encoding declared.
*/
String getInputEncoding();
/**
* Set new value for entity replacement text as defined in
* XML 1.0 Section 4.5
* Construction of Internal Entity Replacement Text.
* If FEATURE_PROCESS_DOCDECL or FEATURE_VALIDATION are set, calling this
* function will result in an exception -- when processing of DOCDECL is
* enabled, there is no need to the entity replacement text manually.
*
*
The motivation for this function is to allow very small
* implementations of XMLPULL that will work in J2ME environments.
* Though these implementations may not be able to process the document type
* declaration, they still can work with known DTDs by using this function.
*
*
Please notes: The given value is used literally as replacement text
* and it corresponds to declaring entity in DTD that has all special characters
* escaped: left angle bracket is replaced with <, ampersnad with &
* and so on.
*
*
Note: The given value is the literal replacement text and must not
* contain any other entity reference (if it contains any entity reference
* there will be no further replacement).
*
*
Note: The list of pre-defined entity names will
* always contain standard XML entities such as
* amp (&), lt (<), gt (>), quot ("), and apos (').
* Those cannot be redefined by this method!
*
* @see #setInput
* @see #FEATURE_PROCESS_DOCDECL
* @see #FEATURE_VALIDATION
*/
void defineEntityReplacementText( String entityName,
String replacementText ) throws XmlPullParserException;
/**
* Returns the numbers of elements in the namespace stack for the given
* depth.
* If namespaces are not enabled, 0 is returned.
*
*
NOTE: when parser is on END_TAG then it is allowed to call
* this function with getDepth()+1 argument to retrieve position of namespace
* prefixes and URIs that were declared on corresponding START_TAG.
*
NOTE: to retrieve lsit of namespaces declared in current element:
* XmlPullParser pp = ...
* int nsStart = pp.getNamespaceCount(pp.getDepth()-1);
* int nsEnd = pp.getNamespaceCount(pp.getDepth());
* for (int i = nsStart; i < nsEnd; i++) {
* String prefix = pp.getNamespacePrefix(i);
* String ns = pp.getNamespaceUri(i);
* // ...
* }
*
*
* @see #getNamespacePrefix
* @see #getNamespaceUri
* @see #getNamespace()
* @see #getNamespace(String)
*/
int getNamespaceCount(int depth) throws XmlPullParserException;
/**
* Returns the namespace prefixe for the given position
* in the namespace stack.
* Default namespace declaration (xmlns='...') will have null as prefix.
* If the given index is out of range, an exception is thrown.
* Please note: when the parser is on an END_TAG,
* namespace prefixes that were declared
* in the corresponding START_TAG are still accessible
* although they are no longer in scope.
*/
String getNamespacePrefix(int pos) throws XmlPullParserException;
/**
* Returns the namespace URI for the given position in the
* namespace stack
* If the position is out of range, an exception is thrown.
*
NOTE: when parser is on END_TAG then namespace prefixes that were declared
* in corresponding START_TAG are still accessible even though they are not in scope
*/
String getNamespaceUri(int pos) throws XmlPullParserException;
/**
* Returns the URI corresponding to the given prefix,
* depending on current state of the parser.
*
*
If the prefix was not declared in the current scope,
* null is returned. The default namespace is included
* in the namespace table and is available via
* getNamespace (null).
*
*
This method is a convenience method for
*
*
* for (int i = getNamespaceCount(getDepth ())-1; i >= 0; i--) {
* if (getNamespacePrefix(i).equals( prefix )) {
* return getNamespaceUri(i);
* }
* }
* return null;
*
*
* Please note: parser implementations
* may provide more efifcient lookup, e.g. using a Hashtable.
* The 'xml' prefix is bound to "http://www.w3.org/XML/1998/namespace", as
* defined in the
* Namespaces in XML
* specification. Analogous, the 'xmlns' prefix is resolved to
* http://www.w3.org/2000/xmlns/
*
* @see #getNamespaceCount
* @see #getNamespacePrefix
* @see #getNamespaceUri
*/
String getNamespace (String prefix);
// --------------------------------------------------------------------------
// miscellaneous reporting methods
/**
* Returns the current depth of the element.
* Outside the root element, the depth is 0. The
* depth is incremented by 1 when a start tag is reached.
* The depth is decremented AFTER the end tag
* event was observed.
*
*
* <!-- outside --> 0
* <root> 1
* sometext 1
* <foobar> 2
* </foobar> 2
* </root> 1
* <!-- outside --> 0
*
*/
int getDepth();
/**
* Returns a short text describing the current parser state, including
* the position, a
* description of the current event and the data source if known.
* This method is especially useful to provide meaningful
* error messages and for debugging purposes.
*/
String getPositionDescription ();
/**
* Returns the current line number, starting from 1.
* When the parser does not know the current line number
* or can not determine it, -1 is returned (e.g. for WBXML).
*
* @return current line number or -1 if unknown.
*/
int getLineNumber();
/**
* Returns the current column number, starting from 0.
* When the parser does not know the current column number
* or can not determine it, -1 is returned (e.g. for WBXML).
*
* @return current column number or -1 if unknown.
*/
int getColumnNumber();
// --------------------------------------------------------------------------
// TEXT related methods
/**
* Checks whether the current TEXT event contains only whitespace
* characters.
* For IGNORABLE_WHITESPACE, this is always true.
* For TEXT and CDSECT, false is returned when the current event text
* contains at least one non-white space character. For any other
* event type an exception is thrown.
*
* Please note: non-validating parsers are not
* able to distinguish whitespace and ignorable whitespace,
* except from whitespace outside the root element. Ignorable
* whitespace is reported as separate event, which is exposed
* via nextToken only.
*
*/
boolean isWhitespace() throws XmlPullParserException;
/**
* Returns the text content of the current event as String.
* The value returned depends on current event type,
* for example for TEXT event it is element content
* (this is typical case when next() is used).
*
* See description of nextToken() for detailed description of
* possible returned values for different types of events.
*
*
NOTE: in case of ENTITY_REF, this method returns
* the entity replacement text (or null if not available). This is
* the only case where
* getText() and getTextCharacters() return different values.
*
* @see #getEventType
* @see #next
* @see #nextToken
*/
String getText ();
/**
* Returns the buffer that contains the text of the current event,
* as well as the start offset and length relevant for the current
* event. See getText(), next() and nextToken() for description of possible returned values.
*
*
Please note: this buffer must not
* be modified and its content MAY change after a call to
* next() or nextToken(). This method will always return the
* same value as getText(), except for ENTITY_REF. In the case
* of ENTITY ref, getText() returns the replacement text and
* this method returns the actual input buffer containing the
* entity name.
* If getText() returns null, this method returns null as well and
* the values returned in the holder array MUST be -1 (both start
* and length).
*
* @see #getText
* @see #next
* @see #nextToken
*
* @param holderForStartAndLength Must hold an 2-element int array
* into which the start offset and length values will be written.
* @return char buffer that contains the text of the current event
* (null if the current event has no text associated).
*/
char[] getTextCharacters(int [] holderForStartAndLength);
// --------------------------------------------------------------------------
// START_TAG / END_TAG shared methods
/**
* Returns the namespace URI of the current element.
* The default namespace is represented
* as empty string.
* If namespaces are not enabled, an empty String ("") is always returned.
* The current event must be START_TAG or END_TAG; otherwise,
* null is returned.
*/
String getNamespace ();
/**
* For START_TAG or END_TAG events, the (local) name of the current
* element is returned when namespaces are enabled. When namespace
* processing is disabled, the raw name is returned.
* For ENTITY_REF events, the entity name is returned.
* If the current event is not START_TAG, END_TAG, or ENTITY_REF,
* null is returned.
*
Please note: To reconstruct the raw element name
* when namespaces are enabled and the prefix is not null,
* you will need to add the prefix and a colon to localName..
*
*/
String getName();
/**
* Returns the prefix of the current element.
* If the element is in the default namespace (has no prefix),
* null is returned.
* If namespaces are not enabled, or the current event
* is not START_TAG or END_TAG, null is returned.
*/
String getPrefix();
/**
* Returns true if the current event is START_TAG and the tag
* is degenerated
* (e.g. <foobar/>).
*
NOTE: if the parser is not on START_TAG, an exception
* will be thrown.
*/
boolean isEmptyElementTag() throws XmlPullParserException;
// --------------------------------------------------------------------------
// START_TAG Attributes retrieval methods
/**
* Returns the number of attributes of the current start tag, or
* -1 if the current event type is not START_TAG
*
* @see #getAttributeNamespace
* @see #getAttributeName
* @see #getAttributePrefix
* @see #getAttributeValue
*/
int getAttributeCount();
/**
* Returns the namespace URI of the attribute
* with the given index (starts from 0).
* Returns an empty string ("") if namespaces are not enabled
* or the attribute has no namespace.
* Throws an IndexOutOfBoundsException if the index is out of range
* or the current event type is not START_TAG.
*
*
NOTE: if FEATURE_REPORT_NAMESPACE_ATTRIBUTES is set
* then namespace attributes (xmlns:ns='...') must be reported
* with namespace
* http://www.w3.org/2000/xmlns/
* (visit this URL for description!).
* The default namespace attribute (xmlns="...") will be reported with empty namespace.
*
NOTE:The xml prefix is bound as defined in
* Namespaces in XML
* specification to "http://www.w3.org/XML/1998/namespace".
*
* @param index zero based index of attribute
* @return attribute namespace,
* empty string ("") is returned if namesapces processing is not enabled or
* namespaces processing is enabled but attribute has no namespace (it has no prefix).
*/
String getAttributeNamespace (int index);
/**
* Returns the local name of the specified attribute
* if namespaces are enabled or just attribute name if namespaces are disabled.
* Throws an IndexOutOfBoundsException if the index is out of range
* or current event type is not START_TAG.
*
* @param index zero based index of attribute
* @return attribute name (null is never returned)
*/
String getAttributeName (int index);
/**
* Returns the prefix of the specified attribute
* Returns null if the element has no prefix.
* If namespaces are disabled it will always return null.
* Throws an IndexOutOfBoundsException if the index is out of range
* or current event type is not START_TAG.
*
* @param index zero based index of attribute
* @return attribute prefix or null if namespaces processing is not enabled.
*/
String getAttributePrefix(int index);
/**
* Returns the type of the specified attribute
* If parser is non-validating it MUST return CDATA.
*
* @param index zero based index of attribute
* @return attribute type (null is never returned)
*/
String getAttributeType(int index);
/**
* Returns if the specified attribute was not in input was declared in XML.
* If parser is non-validating it MUST always return false.
* This information is part of XML infoset:
*
* @param index zero based index of attribute
* @return false if attribute was in input
*/
boolean isAttributeDefault(int index);
/**
* Returns the given attributes value.
* Throws an IndexOutOfBoundsException if the index is out of range
* or current event type is not START_TAG.
*
*
NOTE: attribute value must be normalized
* (including entity replacement text if PROCESS_DOCDECL is false) as described in
* XML 1.0 section
* 3.3.3 Attribute-Value Normalization
*
* @see #defineEntityReplacementText
*
* @param index zero based index of attribute
* @return value of attribute (null is never returned)
*/
String getAttributeValue(int index);
/**
* Returns the attributes value identified by namespace URI and namespace localName.
* If namespaces are disabled namespace must be null.
* If current event type is not START_TAG then IndexOutOfBoundsException will be thrown.
*
*
NOTE: attribute value must be normalized
* (including entity replacement text if PROCESS_DOCDECL is false) as described in
* XML 1.0 section
* 3.3.3 Attribute-Value Normalization
*
* @see #defineEntityReplacementText
*
* @param namespace Namespace of the attribute if namespaces are enabled otherwise must be null
* @param name If namespaces enabled local name of attribute otherwise just attribute name
* @return value of attribute or null if attribute with given name does not exist
*/
String getAttributeValue(String namespace,
String name);
// --------------------------------------------------------------------------
// actual parsing methods
/**
* Returns the type of the current event (START_TAG, END_TAG, TEXT, etc.)
*
* @see #next()
* @see #nextToken()
*/
int getEventType()
throws XmlPullParserException;
/**
* Get next parsing event - element content wil be coalesced and only one
* TEXT event must be returned for whole element content
* (comments and processing instructions will be ignored and emtity references
* must be expanded or exception mus be thrown if entity reerence can not be exapnded).
* If element content is empty (content is "") then no TEXT event will be reported.
*
*
NOTE: empty element (such as <tag/>) will be reported
* with two separate events: START_TAG, END_TAG - it must be so to preserve
* parsing equivalency of empty element to <tag></tag>.
* (see isEmptyElementTag ())
*
* @see #isEmptyElementTag
* @see #START_TAG
* @see #TEXT
* @see #END_TAG
* @see #END_DOCUMENT
*/
int next()
throws XmlPullParserException, IOException;
/**
* This method works similarly to next() but will expose
* additional event types (COMMENT, CDSECT, DOCDECL, ENTITY_REF, PROCESSING_INSTRUCTION, or
* IGNORABLE_WHITESPACE) if they are available in input.
*
*
If special feature
* FEATURE_XML_ROUNDTRIP
* (identified by URI: http://xmlpull.org/v1/doc/features.html#xml-roundtrip)
* is enabled it is possible to do XML document round trip ie. reproduce
* exectly on output the XML input using getText():
* returned content is always unnormalized (exactly as in input).
* Otherwise returned content is end-of-line normalized as described
* XML 1.0 End-of-Line Handling
* and. Also when this feature is enabled exact content of START_TAG, END_TAG,
* DOCDECL and PROCESSING_INSTRUCTION is available.
*
*
Here is the list of tokens that can be returned from nextToken()
* and what getText() and getTextCharacters() returns:
* - START_DOCUMENT
- null
*
- END_DOCUMENT
- null
*
- START_TAG
- null unless FEATURE_XML_ROUNDTRIP
* enabled and then returns XML tag, ex: <tag attr='val'>
*
- END_TAG
- null unless FEATURE_XML_ROUNDTRIP
* id enabled and then returns XML tag, ex: </tag>
*
- TEXT
- return element content.
*
Note: that element content may be delivered in multiple consecutive TEXT events.
* - IGNORABLE_WHITESPACE
- return characters that are determined to be ignorable white
* space. If the FEATURE_XML_ROUNDTRIP is enabled all whitespace content outside root
* element will always reported as IGNORABLE_WHITESPACE otherise rteporting is optional.
*
Note: that element content may be delevered in multiple consecutive IGNORABLE_WHITESPACE events.
* - CDSECT
-
* return text inside CDATA
* (ex. 'fo<o' from <!CDATA[fo<o]]>)
*
- PROCESSING_INSTRUCTION
-
* if FEATURE_XML_ROUNDTRIP is true
* return exact PI content ex: 'pi foo' from <?pi foo?>
* otherwise it may be exact PI content or concatenation of PI target,
* space and data so for example for
* <?target data?> string "target data" may
* be returned if FEATURE_XML_ROUNDTRIP is false.
*
- COMMENT
- return comment content ex. 'foo bar' from <!--foo bar-->
*
- ENTITY_REF
- getText() MUST return entity replacement text if PROCESS_DOCDECL is false
* otherwise getText() MAY return null,
* additionally getTextCharacters() MUST return entity name
* (for example 'entity_name' for &entity_name;).
*
NOTE: this is the only place where value returned from getText() and
* getTextCharacters() are different
*
NOTE: it is user responsibility to resolve entity reference
* if PROCESS_DOCDECL is false and there is no entity replacement text set in
* defineEntityReplacementText() method (getText() will be null)
*
NOTE: character entities (ex.  ) and standard entities such as
* & < > " ' are reported as well
* and are not reported as TEXT tokens but as ENTITY_REF tokens!
* This requirement is added to allow to do roundtrip of XML documents!
* - DOCDECL
-
* if FEATURE_XML_ROUNDTRIP is true or PROCESS_DOCDECL is false
* then return what is inside of DOCDECL for example it returns:
* " titlepage SYSTEM "http://www.foo.bar/dtds/typo.dtd"
* [<!ENTITY % active.links "INCLUDE">]"
* for input document that contained:
* <!DOCTYPE titlepage SYSTEM "http://www.foo.bar/dtds/typo.dtd"
* [<!ENTITY % active.links "INCLUDE">]>
* otherwise if FEATURE_XML_ROUNDTRIP is false and PROCESS_DOCDECL is true
* then what is returned is undefined (it may be even null)
*
*
*
* NOTE: there is no gurantee that there will only one TEXT or
* IGNORABLE_WHITESPACE event from nextToken() as parser may chose to deliver element content in
* multiple tokens (dividing element content into chunks)
*
*
NOTE: whether returned text of token is end-of-line normalized
* is depending on FEATURE_XML_ROUNDTRIP.
*
*
NOTE: XMLDecl (<?xml ...?>) is not reported but its content
* is available through optional properties (see class description above).
*
* @see #next
* @see #START_TAG
* @see #TEXT
* @see #END_TAG
* @see #END_DOCUMENT
* @see #COMMENT
* @see #DOCDECL
* @see #PROCESSING_INSTRUCTION
* @see #ENTITY_REF
* @see #IGNORABLE_WHITESPACE
*/
int nextToken()
throws XmlPullParserException, IOException;
//-----------------------------------------------------------------------------
// utility methods to mak XML parsing easier ...
/**
* Test if the current event is of the given type and if the
* namespace and name do match. null will match any namespace
* and any name. If the test is not passed, an exception is
* thrown. The exception text indicates the parser position,
* the expected event and the current event that is not meeting the
* requirement.
*
*
Essentially it does this
*
* if (type != getEventType()
* || (namespace != null && !namespace.equals( getNamespace () ) )
* || (name != null && !name.equals( getName() ) ) )
* throw new XmlPullParserException( "expected "+ TYPES[ type ]+getPositionDescription());
*
*/
void require(int type, String namespace, String name)
throws XmlPullParserException, IOException;
/**
* If current event is START_TAG then if next element is TEXT then element content is returned
* or if next event is END_TAG then empty string is returned, otherwise exception is thrown.
* After calling this function successfully parser will be positioned on END_TAG.
*
* The motivation for this function is to allow to parse consistently both
* empty elements and elements that has non empty content, for example for input:
* - <tag>foo</tag>
*
- <tag></tag> (which is equivalent to <tag/>
* both input can be parsed with the same code:
*
* p.nextTag()
* p.requireEvent(p.START_TAG, "", "tag");
* String content = p.nextText();
* p.requireEvent(p.END_TAG, "", "tag");
*
* This function together with nextTag make it very easy to parse XML that has
* no mixed content.
*
*
* Essentially it does this
*
* if(getEventType() != START_TAG) {
* throw new XmlPullParserException(
* "parser must be on START_TAG to read next text", this, null);
* }
* int eventType = next();
* if(eventType == TEXT) {
* String result = getText();
* eventType = next();
* if(eventType != END_TAG) {
* throw new XmlPullParserException(
* "event TEXT it must be immediately followed by END_TAG", this, null);
* }
* return result;
* } else if(eventType == END_TAG) {
* return "";
* } else {
* throw new XmlPullParserException(
* "parser must be on START_TAG or TEXT to read text", this, null);
* }
*
*/
String nextText() throws XmlPullParserException, IOException;
/**
* Call next() and return event if it is START_TAG or END_TAG
* otherwise throw an exception.
* It will skip whitespace TEXT before actual tag if any.
*
* essentially it does this
*
* int eventType = next();
* if(eventType == TEXT && isWhitespace()) { // skip whitespace
* eventType = next();
* }
* if (eventType != START_TAG && eventType != END_TAG) {
* throw new XmlPullParserException("expected start or end tag", this, null);
* }
* return eventType;
*
*/
int nextTag() throws XmlPullParserException, IOException;
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/pull/XmlPullParserException.java 0000664 0000000 0000000 00000004357 11660305702 0032652 0 ustar 00root root 0000000 0000000 /* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/
// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/)
package org.codehaus.plexus.util.xml.pull;
/**
* This exception is thrown to signal XML Pull Parser related faults.
*
* @author Aleksander Slominski
*/
public class XmlPullParserException extends Exception {
protected Throwable detail;
protected int row = -1;
protected int column = -1;
/* public XmlPullParserException() {
}*/
public XmlPullParserException(String s) {
super(s);
}
/*
public XmlPullParserException(String s, Throwable thrwble) {
super(s);
this.detail = thrwble;
}
public XmlPullParserException(String s, int row, int column) {
super(s);
this.row = row;
this.column = column;
}
*/
public XmlPullParserException(String msg, XmlPullParser parser, Throwable chain) {
super ((msg == null ? "" : msg+" ")
+ (parser == null ? "" : "(position:"+parser.getPositionDescription()+") ")
+ (chain == null ? "" : "caused by: "+chain));
if (parser != null) {
this.row = parser.getLineNumber();
this.column = parser.getColumnNumber();
}
this.detail = chain;
}
public Throwable getDetail() { return detail; }
// public void setDetail(Throwable cause) { this.detail = cause; }
public int getLineNumber() { return row; }
public int getColumnNumber() { return column; }
/*
public String getMessage() {
if(detail == null)
return super.getMessage();
else
return super.getMessage() + "; nested exception is: \n\t"
+ detail.getMessage();
}
*/
//NOTE: code that prints this and detail is difficult in J2ME
public void printStackTrace() {
if (detail == null) {
super.printStackTrace();
} else {
synchronized(System.err) {
System.err.println(super.getMessage() + "; nested exception is:");
detail.printStackTrace();
}
}
}
}
plexus-utils2-2.0.5/src/main/java/org/codehaus/plexus/util/xml/pull/XmlSerializer.java 0000664 0000000 0000000 00000035255 11660305702 0031014 0 ustar 00root root 0000000 0000000 /* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/
// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/)
package org.codehaus.plexus.util.xml.pull;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
/**
* Define an interface to serialization of XML Infoset.
* This interface abstracts away if serialized XML is XML 1.0 compatible text or
* other formats of XML 1.0 serializations (such as binary XML for example with WBXML).
*
* PLEASE NOTE: This interface will be part of XmlPull 1.2 API.
* It is included as basis for discussion. It may change in any way.
*
*
Exceptions that may be thrown are: IOException or runtime exception
* (more runtime exceptions can be thrown but are not declared and as such
* have no semantics defined for this interface):
*
* - IllegalArgumentException - for almost all methods to signal that
* argument is illegal
*
- IllegalStateException - to signal that call has good arguments but
* is not expected here (violation of contract) and for features/properties
* when requesting setting unimplemented feature/property
* (UnsupportedOperationException would be better but it is not in MIDP)
*
*
* NOTE: writing CDSECT, ENTITY_REF, IGNORABLE_WHITESPACE,
* PROCESSING_INSTRUCTION, COMMENT, and DOCDECL in some implementations
* may not be supported (for example when serializing to WBXML).
* In such case IllegalStateException will be thrown and it is recommended
* to use an optional feature to signal that implementation is not
* supporting this kind of output.
*/
public interface XmlSerializer {
/**
* Set feature identified by name (recommended to be URI for uniqueness).
* Some well known optional features are defined in
*
* http://www.xmlpull.org/v1/doc/features.html.
*
* If feature is not recognized or can not be set
* then IllegalStateException MUST be thrown.
*
* @exception IllegalStateException If the feature is not supported or can not be set
*/
void setFeature(String name,
boolean state)
throws IllegalArgumentException, IllegalStateException;
/**
* Return the current value of the feature with given name.
*
NOTE: unknown properties are always returned as null
*
* @param name The name of feature to be retrieved.
* @return The value of named feature.
* @exception IllegalArgumentException if feature string is null
*/
boolean getFeature(String name);
/**
* Set the value of a property.
* (the property name is recommended to be URI for uniqueness).
* Some well known optional properties are defined in
*
* http://www.xmlpull.org/v1/doc/properties.html.
*
* If property is not recognized or can not be set
* then IllegalStateException MUST be thrown.
*
* @exception IllegalStateException if the property is not supported or can not be set
*/
void setProperty(String name,
Object value)
throws IllegalArgumentException, IllegalStateException;
/**
* Look up the value of a property.
*
* The property name is any fully-qualified URI. I
*
NOTE: unknown properties are always returned as null
*
* @param name The name of property to be retrieved.
* @return The value of named property.
*/
Object getProperty(String name);
/**
* Set to use binary output stream with given encoding.
*/
void setOutput (OutputStream os, String encoding)
throws IOException, IllegalArgumentException, IllegalStateException;
/**
* Set the output to the given writer.
* WARNING no information about encoding is available!
*/
void setOutput (Writer writer)
throws IOException, IllegalArgumentException, IllegalStateException;
/**
* Write <?xml declaration with encoding (if encoding not null)
* and standalone flag (if standalone not null)
* This method can only be called just after setOutput.
*/
void startDocument (String encoding, Boolean standalone)
throws IOException, IllegalArgumentException, IllegalStateException;
/**
* Finish writing. All unclosed start tags will be closed and output
* will be flushed. After calling this method no more output can be
* serialized until next call to setOutput()
*/
void endDocument ()
throws IOException, IllegalArgumentException, IllegalStateException;
/**
* Binds the given prefix to the given namespace.
* This call is valid for the next element including child elements.
* The prefix and namespace MUST be always declared even if prefix
* is not used in element (startTag() or attribute()) - for XML 1.0
* it must result in declaring xmlns:prefix='namespace'
* (or xmlns:prefix="namespace"
depending what character is used
* to quote attribute value).
*
*
NOTE: this method MUST be called directly before startTag()
* and if anything but startTag() or setPrefix() is called next there will be exception.
*
NOTE: prefixes "xml" and "xmlns" are already bound
* and can not be redefined see:
* Namespaces in XML Errata.
*
NOTE: to set default namespace use as prefix empty string.
*
* @param prefix must be not null (or IllegalArgumentException is thrown)
* @param namespace must be not null
*/
void setPrefix (String prefix, String namespace)
throws IOException, IllegalArgumentException, IllegalStateException;
/**
* Return namespace that corresponds to given prefix
* If there is no prefix bound to this namespace return null
* but if generatePrefix is false then return generated prefix.
*
*
NOTE: if the prefix is empty string "" and default namespace is bound
* to this prefix then empty string ("") is returned.
*
*
NOTE: prefixes "xml" and "xmlns" are already bound
* will have values as defined
* Namespaces in XML specification
*/
String getPrefix (String namespace, boolean generatePrefix)
throws IllegalArgumentException;
/**
* Returns the current depth of the element.
* Outside the root element, the depth is 0. The
* depth is incremented by 1 when startTag() is called.
* The depth is decremented after the call to endTag()
* event was observed.
*
*
* <!-- outside --> 0
* <root> 1
* sometext 1
* <foobar> 2
* </foobar> 2
* </root> 1
* <!-- outside --> 0
*
*/
int getDepth();
/**
* Returns the namespace URI of the current element as set by startTag().
*
* NOTE: that means in particular that:
* - if there was startTag("", ...) then getNamespace() returns ""
*
- if there was startTag(null, ...) then getNamespace() returns null
*
*
* @return namespace set by startTag() that is currently in scope
*/
String getNamespace ();
/**
* Returns the name of the current element as set by startTag().
* It can only be null before first call to startTag()
* or when last endTag() is called to close first startTag().
*
* @return namespace set by startTag() that is currently in scope
*/
String getName();
/**
* Writes a start tag with the given namespace and name.
* If there is no prefix defined for the given namespace,
* a prefix will be defined automatically.
* The explicit prefixes for namespaces can be established by calling setPrefix()
* immediately before this method.
* If namespace is null no namespace prefix is printed but just name.
* If namespace is empty string then serializer will make sure that
* default empty namespace is declared (in XML 1.0 xmlns='')
* or throw IllegalStateException if default namespace is already bound
* to non-empty string.
*/
XmlSerializer startTag (String namespace, String name)
throws IOException, IllegalArgumentException, IllegalStateException;
/**
* Write an attribute. Calls to attribute() MUST follow a call to
* startTag() immediately. If there is no prefix defined for the
* given namespace, a prefix will be defined automatically.
* If namespace is null or empty string
* no namespace prefix is printed but just name.
*/
XmlSerializer attribute (String namespace, String name, String value)
throws IOException, IllegalArgumentException, IllegalStateException;
/**
* Write end tag. Repetition of namespace and name is just for avoiding errors.
* Background: in kXML endTag had no arguments, and non matching tags were
* very difficult to find...
* If namespace is null no namespace prefix is printed but just name.
* If namespace is empty string then serializer will make sure that
* default empty namespace is declared (in XML 1.0 xmlns='').
*/
XmlSerializer endTag (String namespace, String name)
throws IOException, IllegalArgumentException, IllegalStateException;
// /**
// * Writes a start tag with the given namespace and name.
// *
If there is no prefix defined (prefix == null) for the given namespace,
// * a prefix will be defined automatically.
// *
If explicit prefixes is passed (prefix != null) then it will be used
// *and namespace declared if not already declared or
// * throw IllegalStateException the same prefix was already set on this
// * element (setPrefix()) and was bound to different namespace.
// *
If namespace is null then prefix must be null too or IllegalStateException is thrown.
// *
If namespace is null then no namespace prefix is printed but just name.
// *
If namespace is empty string then serializer will make sure that
// * default empty namespace is declared (in XML 1.0 xmlns='')
// * or throw IllegalStateException if default namespace is already bound
// * to non-empty string.
// */
// XmlSerializer startTag (String prefix, String namespace, String name)
// throws IOException, IllegalArgumentException, IllegalStateException;
//
// /**
// * Write an attribute. Calls to attribute() MUST follow a call to
// * startTag() immediately.
// *
If there is no prefix defined (prefix == null) for the given namespace,
// * a prefix will be defined automatically.
// *
If explicit prefixes is passed (prefix != null) then it will be used
// * and namespace declared if not already declared or
// * throw IllegalStateException the same prefix was already set on this
// * element (setPrefix()) and was bound to different namespace.
// *
If namespace is null then prefix must be null too or IllegalStateException is thrown.
// *
If namespace is null then no namespace prefix is printed but just name.
// *
If namespace is empty string then serializer will make sure that
// * default empty namespace is declared (in XML 1.0 xmlns='')
// * or throw IllegalStateException if default namespace is already bound
// * to non-empty string.
// */
// XmlSerializer attribute (String prefix, String namespace, String name, String value)
// throws IOException, IllegalArgumentException, IllegalStateException;
//
// /**
// * Write end tag. Repetition of namespace, prefix, and name is just for avoiding errors.
// *
If namespace or name arguments are different from corresponding startTag call
// * then IllegalArgumentException is thrown, if prefix argument is not null and is different
// * from corresponding starTag then IllegalArgumentException is thrown.
// *
If namespace is null then prefix must be null too or IllegalStateException is thrown.
// *
If namespace is null then no namespace prefix is printed but just name.
// *
If namespace is empty string then serializer will make sure that
// * default empty namespace is declared (in XML 1.0 xmlns='').
// *
Background: in kXML endTag had no arguments, and non matching tags were
// * very difficult to find...
// */
// ALEK: This is really optional as prefix in end tag MUST correspond to start tag but good for error checking
// XmlSerializer endTag (String prefix, String namespace, String name)
// throws IOException, IllegalArgumentException, IllegalStateException;
/**
* Writes text, where special XML chars are escaped automatically
*/
XmlSerializer text (String text)
throws IOException, IllegalArgumentException, IllegalStateException;
/**
* Writes text, where special XML chars are escaped automatically
*/
XmlSerializer text (char [] buf, int start, int len)
throws IOException, IllegalArgumentException, IllegalStateException;
void cdsect (String text)
throws IOException, IllegalArgumentException, IllegalStateException;
void entityRef (String text) throws IOException,
IllegalArgumentException, IllegalStateException;
void processingInstruction (String text)
throws IOException, IllegalArgumentException, IllegalStateException;
void comment (String text)
throws IOException, IllegalArgumentException, IllegalStateException;
void docdecl (String text)
throws IOException, IllegalArgumentException, IllegalStateException;
void ignorableWhitespace (String text)
throws IOException, IllegalArgumentException, IllegalStateException;
/**
* Write all pending output to the stream.
* If method startTag() or attribute() was called then start tag is closed (final >)
* before flush() is called on underlying output stream.
*
* NOTE: if there is need to close start tag
* (so no more attribute() calls are allowed) but without flushing output
* call method text() with empty string (text("")).
*
*/
void flush ()
throws IOException;
}
plexus-utils2-2.0.5/src/main/javadoc/ 0000775 0000000 0000000 00000000000 11660305702 0017400 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/javadoc/org/ 0000775 0000000 0000000 00000000000 11660305702 0020167 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/javadoc/org/codehaus/ 0000775 0000000 0000000 00000000000 11660305702 0021762 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/javadoc/org/codehaus/plexus/ 0000775 0000000 0000000 00000000000 11660305702 0023302 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/javadoc/org/codehaus/plexus/util/ 0000775 0000000 0000000 00000000000 11660305702 0024257 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/javadoc/org/codehaus/plexus/util/cli/ 0000775 0000000 0000000 00000000000 11660305702 0025026 5 ustar 00root root 0000000 0000000 plexus-utils2-2.0.5/src/main/javadoc/org/codehaus/plexus/util/cli/package.html 0000664 0000000 0000000 00000000046 11660305702 0027307 0 ustar 00root root 0000000 0000000
Command-line utilities.