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.
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.
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 benull, indicating that all files should be
@@ -253,7 +253,7 @@ public 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 benull, indicating that no files should be
diff --git a/src/main/java/org/codehaus/plexus/util/FileUtils.java b/src/main/java/org/codehaus/plexus/util/FileUtils.java
index cac82ba3..4dca8b3c 100644
--- a/src/main/java/org/codehaus/plexus/util/FileUtils.java
+++ b/src/main/java/org/codehaus/plexus/util/FileUtils.java
@@ -389,6 +389,8 @@ private static InputStreamReader getInputStreamReader( File file, String encodin
* @param fileName The path of the file to write.
* @param data The content to write to the file.
* @throws IOException if any
+ * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(encoding),
+ * StandardOpenOption.APPEND, StandardOpenOption.CREATE)}
*/
public static void fileAppend( String fileName, String data )
throws IOException
@@ -403,11 +405,14 @@ public static void fileAppend( String fileName, String data )
* @param encoding The encoding of the file.
* @param data The content to write to the file.
* @throws IOException if any
+ * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(encoding),
+ * StandardOpenOption.APPEND, StandardOpenOption.CREATE)}
*/
public static void fileAppend( String fileName, String encoding, String data )
throws IOException
{
- try ( OutputStream out = Files.newOutputStream( Paths.get(fileName), StandardOpenOption.APPEND ) )
+ try ( OutputStream out = Files.newOutputStream( Paths.get(fileName),
+ StandardOpenOption.APPEND, StandardOpenOption.CREATE ) )
{
if ( encoding != null )
{
diff --git a/src/main/java/org/codehaus/plexus/util/SelectorUtils.java b/src/main/java/org/codehaus/plexus/util/SelectorUtils.java
index de39f4fe..234a92c5 100644
--- a/src/main/java/org/codehaus/plexus/util/SelectorUtils.java
+++ b/src/main/java/org/codehaus/plexus/util/SelectorUtils.java
@@ -253,21 +253,32 @@ public static boolean matchPath( String pattern, String str, String separator, b
{
if ( isRegexPrefixedPattern( pattern ) )
{
- pattern =
+ String localPattern =
pattern.substring( REGEX_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
- return str.matches( pattern );
+ return str.matches( localPattern );
}
else
{
- if ( isAntPrefixedPattern( pattern ) )
- {
- pattern = pattern.substring( ANT_HANDLER_PREFIX.length(),
- pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
- }
+ String localPattern = isAntPrefixedPattern( pattern )
+ ? pattern.substring( ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() )
+ : pattern;
+ final String osRelatedPath = toOSRelatedPath( str, separator );
+ final String osRelatedPattern = toOSRelatedPath( localPattern, separator );
+ return matchAntPathPattern( osRelatedPattern, osRelatedPath, separator, isCaseSensitive );
+ }
+ }
- return matchAntPathPattern( pattern, str, separator, isCaseSensitive );
+ private static String toOSRelatedPath( String pattern, String separator )
+ {
+ if ( "/".equals( separator ) )
+ {
+ return pattern.replace( "\\", separator );
+ }
+ if ( "\\".equals( separator ) ) {
+ return pattern.replace( "/", separator );
}
+ return pattern;
}
static boolean isRegexPrefixedPattern( String pattern )
diff --git a/src/main/java/org/codehaus/plexus/util/io/CachingOutputStream.java b/src/main/java/org/codehaus/plexus/util/io/CachingOutputStream.java
new file mode 100644
index 00000000..744e6f06
--- /dev/null
+++ b/src/main/java/org/codehaus/plexus/util/io/CachingOutputStream.java
@@ -0,0 +1,173 @@
+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.IOException;
+import java.io.OutputStream;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.FileTime;
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * Caching OutputStream to avoid overwriting a file with
+ * the same content.
+ */
+public class CachingOutputStream extends OutputStream
+{
+ private final Path path;
+ private FileChannel channel;
+ private ByteBuffer readBuffer;
+ private ByteBuffer writeBuffer;
+ private boolean modified;
+
+ public CachingOutputStream( File path ) throws IOException
+ {
+ this( Objects.requireNonNull( path ).toPath() );
+ }
+
+ public CachingOutputStream( Path path ) throws IOException
+ {
+ this( path, 32 * 1024 );
+ }
+
+ public CachingOutputStream( Path path, int bufferSize ) throws IOException
+ {
+ this.path = Objects.requireNonNull( path );
+ this.channel = FileChannel.open( path,
+ StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE );
+ this.readBuffer = ByteBuffer.allocate( bufferSize );
+ this.writeBuffer = ByteBuffer.allocate( bufferSize );
+ }
+
+ @Override
+ public void write( int b ) throws IOException
+ {
+ if ( writeBuffer.remaining() < 1 )
+ {
+ ( ( Buffer ) writeBuffer ).flip();
+ flushBuffer( writeBuffer );
+ ( ( Buffer ) writeBuffer ).clear();
+ }
+ writeBuffer.put( ( byte ) b );
+ }
+
+ @Override
+ public void write( byte[] b ) throws IOException
+ {
+ write( b, 0, b.length );
+ }
+
+ @Override
+ public void write( byte[] b, int off, int len ) throws IOException
+ {
+ if ( writeBuffer.remaining() < len )
+ {
+ ( ( Buffer ) writeBuffer ).flip();
+ flushBuffer( writeBuffer );
+ ( ( Buffer ) writeBuffer ).clear();
+ }
+ int capacity = writeBuffer.capacity();
+ while ( len >= capacity )
+ {
+ flushBuffer( ByteBuffer.wrap( b, off, capacity ) );
+ off += capacity;
+ len -= capacity;
+ }
+ if ( len > 0 )
+ {
+ writeBuffer.put( b, off, len );
+ }
+ }
+
+ @Override
+ public void flush() throws IOException
+ {
+ ( ( Buffer ) writeBuffer ).flip();
+ flushBuffer( writeBuffer );
+ ( ( Buffer ) writeBuffer ).clear();
+ super.flush();
+ }
+
+ private void flushBuffer( ByteBuffer writeBuffer ) throws IOException
+ {
+ if ( modified )
+ {
+ channel.write( writeBuffer );
+ }
+ else
+ {
+ int len = writeBuffer.remaining();
+ ByteBuffer readBuffer;
+ if ( this.readBuffer.capacity() >= len )
+ {
+ readBuffer = this.readBuffer;
+ ( ( Buffer ) readBuffer ).clear();
+ }
+ else
+ {
+ readBuffer = ByteBuffer.allocate( len );
+ }
+ while ( len > 0 )
+ {
+ int read = channel.read( readBuffer );
+ if ( read <= 0 )
+ {
+ modified = true;
+ channel.position( channel.position() - readBuffer.position() );
+ channel.write( writeBuffer );
+ return;
+ }
+ len -= read;
+ }
+ ( ( Buffer ) readBuffer ).flip();
+ if ( readBuffer.compareTo( writeBuffer ) != 0 )
+ {
+ modified = true;
+ channel.position( channel.position() - readBuffer.remaining() );
+ channel.write( writeBuffer );
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ if ( channel.isOpen() )
+ {
+ flush();
+ long position = channel.position();
+ if ( position != channel.size() )
+ {
+ modified = true;
+ channel.truncate( position );
+ }
+ channel.close();
+ }
+ }
+
+ public boolean isModified()
+ {
+ return modified;
+ }
+}
diff --git a/src/main/java/org/codehaus/plexus/util/io/CachingWriter.java b/src/main/java/org/codehaus/plexus/util/io/CachingWriter.java
new file mode 100644
index 00000000..23cc4411
--- /dev/null
+++ b/src/main/java/org/codehaus/plexus/util/io/CachingWriter.java
@@ -0,0 +1,62 @@
+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.IOException;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Caching Writer to avoid overwriting a file with
+ * the same content.
+ */
+public class CachingWriter extends OutputStreamWriter
+{
+ private final CachingOutputStream cos;
+
+ public CachingWriter( File path, Charset charset ) throws IOException
+ {
+ this( Objects.requireNonNull( path ).toPath(), charset );
+ }
+
+ public CachingWriter( Path path, Charset charset ) throws IOException
+ {
+ this( path, charset, 32 * 1024 );
+ }
+
+ public CachingWriter( Path path, Charset charset, int bufferSize ) throws IOException
+ {
+ this( new CachingOutputStream( path, bufferSize ), charset );
+ }
+
+ private CachingWriter( CachingOutputStream outputStream, Charset charset ) throws IOException
+ {
+ super( outputStream, charset );
+ this.cos = outputStream;
+ }
+
+ public boolean isModified()
+ {
+ return cos.isModified();
+ }
+}
diff --git a/src/main/java/org/codehaus/plexus/util/xml/pull/MXParser.java b/src/main/java/org/codehaus/plexus/util/xml/pull/MXParser.java
index 3874f572..d44c9a7f 100644
--- a/src/main/java/org/codehaus/plexus/util/xml/pull/MXParser.java
+++ b/src/main/java/org/codehaus/plexus/util/xml/pull/MXParser.java
@@ -124,7 +124,7 @@ private String newStringIntern( char[] cbuf, int off, int len )
// private String elValue[];
private int elNamespaceCount[];
- private String fileEncoding = "UTF8";
+ private String fileEncoding = null;
/**
* Make sure that we have enough space to keep element stack if passed size. It will always create one additional
@@ -464,7 +464,7 @@ private void reset()
// System.out.println("reset() called");
location = null;
lineNumber = 1;
- columnNumber = 0;
+ columnNumber = 1;
seenRoot = false;
reachedEnd = false;
eventType = START_DOCUMENT;
@@ -587,8 +587,8 @@ else if ( FEATURE_XML_ROUNDTRIP.equals( name ) )
}
}
- /**
- * Unknown properties are always returned as false
+ /**
+ * Unknown properties are always returned as false
*/
@Override
public boolean getFeature( String name )
@@ -1596,11 +1596,11 @@ else if ( ch == '&' )
}
final int oldStart = posStart + bufAbsoluteStart;
final int oldEnd = posEnd + bufAbsoluteStart;
- final char[] resolvedEntity = parseEntityRef();
+ parseEntityRef();
if ( tokenize )
return eventType = ENTITY_REF;
// check if replacement text can be resolved !!!
- if ( resolvedEntity == null )
+ if ( resolvedEntityRefCharBuf == BUF_NOT_RESOLVED )
{
if ( entityRefName == null )
{
@@ -1628,7 +1628,7 @@ else if ( ch == '&' )
}
// assert usePC == true;
// write into PC replacement text - do merge for replacement text!!!!
- for ( char aResolvedEntity : resolvedEntity )
+ for ( char aResolvedEntity : resolvedEntityRefCharBuf )
{
if ( pcEnd >= pc.length )
{
@@ -2675,9 +2675,28 @@ else if ( ch == '\t' || ch == '\n' || ch == '\r' )
return ch;
}
- private char[] charRefOneCharBuf = new char[1];
+ // state representing that no entity ref have been resolved
+ private static final char[] BUF_NOT_RESOLVED = new char[0];
+
+ // predefined entity refs
+ private static final char[] BUF_LT = new char[] { '<' };
+ private static final char[] BUF_AMP = new char[] { '&' };
+ private static final char[] BUF_GT = new char[] { '>' };
+ private static final char[] BUF_APO = new char[] { '\'' };
+ private static final char[] BUF_QUOT = new char[] { '"' };
- private char[] parseEntityRef()
+ private char[] resolvedEntityRefCharBuf = BUF_NOT_RESOLVED;
+
+ /**
+ * parse Entity Ref, either a character entity or one of the predefined name entities.
+ *
+ * @return the length of the valid found character reference, which may be one of the predefined character reference
+ * names (resolvedEntityRefCharBuf contains the replaced chars). Returns the length of the not found entity
+ * name, otherwise.
+ * @throws XmlPullParserException if invalid XML is detected.
+ * @throws IOException if an I/O error is found.
+ */
+ private int parseCharOrPredefinedEntityRef()
throws XmlPullParserException, IOException
{
// entity reference http://www.w3.org/TR/2000/REC-xml-20001006#NT-Reference
@@ -2686,6 +2705,8 @@ private char[] parseEntityRef()
// ASSUMPTION just after &
entityRefName = null;
posStart = pos;
+ int len = 0;
+ resolvedEntityRefCharBuf = BUF_NOT_RESOLVED;
char ch = more();
if ( ch == '#' )
{
@@ -2750,7 +2771,6 @@ else if ( ch >= 'A' && ch <= 'F' )
ch = more();
}
}
- posEnd = pos - 1;
boolean isValidCodePoint = true;
try
@@ -2759,7 +2779,7 @@ else if ( ch >= 'A' && ch <= 'F' )
isValidCodePoint = isValidCodePoint( codePoint );
if ( isValidCodePoint )
{
- charRefOneCharBuf = Character.toChars( codePoint );
+ resolvedEntityRefCharBuf = Character.toChars( codePoint );
}
}
catch ( IllegalArgumentException e )
@@ -2775,14 +2795,14 @@ else if ( ch >= 'A' && ch <= 'F' )
if ( tokenize )
{
- text = newString( charRefOneCharBuf, 0, charRefOneCharBuf.length );
+ text = newString( resolvedEntityRefCharBuf, 0, resolvedEntityRefCharBuf.length );
}
- return charRefOneCharBuf;
+ len = resolvedEntityRefCharBuf.length;
}
else
{
// [68] EntityRef ::= '&' Name ';'
- // scan anem until ;
+ // scan name until ;
if ( !isNameStartChar( ch ) )
{
throw new XmlPullParserException( "entity reference names can not start with character '"
@@ -2801,17 +2821,15 @@ else if ( ch >= 'A' && ch <= 'F' )
+ printable( ch ) + "'", this, null );
}
}
- posEnd = pos - 1;
// determine what name maps to
- final int len = posEnd - posStart;
+ len = ( pos - 1 ) - posStart;
if ( len == 2 && buf[posStart] == 'l' && buf[posStart + 1] == 't' )
{
if ( tokenize )
{
text = "<";
}
- charRefOneCharBuf[0] = '<';
- return charRefOneCharBuf;
+ resolvedEntityRefCharBuf = BUF_LT;
// if(paramPC || isParserTokenizing) {
// if(pcEnd >= pc.length) ensurePC();
// pc[pcEnd++] = '<';
@@ -2823,8 +2841,7 @@ else if ( len == 3 && buf[posStart] == 'a' && buf[posStart + 1] == 'm' && buf[po
{
text = "&";
}
- charRefOneCharBuf[0] = '&';
- return charRefOneCharBuf;
+ resolvedEntityRefCharBuf = BUF_AMP;
}
else if ( len == 2 && buf[posStart] == 'g' && buf[posStart + 1] == 't' )
{
@@ -2832,8 +2849,7 @@ else if ( len == 2 && buf[posStart] == 'g' && buf[posStart + 1] == 't' )
{
text = ">";
}
- charRefOneCharBuf[0] = '>';
- return charRefOneCharBuf;
+ resolvedEntityRefCharBuf = BUF_GT;
}
else if ( len == 4 && buf[posStart] == 'a' && buf[posStart + 1] == 'p' && buf[posStart + 2] == 'o'
&& buf[posStart + 3] == 's' )
@@ -2842,8 +2858,7 @@ else if ( len == 4 && buf[posStart] == 'a' && buf[posStart + 1] == 'p' && buf[po
{
text = "'";
}
- charRefOneCharBuf[0] = '\'';
- return charRefOneCharBuf;
+ resolvedEntityRefCharBuf = BUF_APO;
}
else if ( len == 4 && buf[posStart] == 'q' && buf[posStart + 1] == 'u' && buf[posStart + 2] == 'o'
&& buf[posStart + 3] == 't' )
@@ -2852,25 +2867,65 @@ else if ( len == 4 && buf[posStart] == 'q' && buf[posStart + 1] == 'u' && buf[po
{
text = "\"";
}
- charRefOneCharBuf[0] = '"';
- return charRefOneCharBuf;
- }
- else
- {
- final char[] result = lookuEntityReplacement( len );
- if ( result != null )
- {
- return result;
- }
+ resolvedEntityRefCharBuf = BUF_QUOT;
}
- if ( tokenize )
- text = null;
- return null;
}
+
+ posEnd = pos;
+
+ return len;
}
/**
- * Check if the provided parameter is a valid Char, according to: {@link https://www.w3.org/TR/REC-xml/#NT-Char}
+ * Parse an entity reference inside the DOCDECL section.
+ *
+ * @throws XmlPullParserException if invalid XML is detected.
+ * @throws IOException if an I/O error is found.
+ */
+ private void parseEntityRefInDocDecl()
+ throws XmlPullParserException, IOException
+ {
+ parseCharOrPredefinedEntityRef();
+ if (usePC) {
+ posStart--; // include in PC the starting '&' of the entity
+ joinPC();
+ }
+
+ if ( resolvedEntityRefCharBuf != BUF_NOT_RESOLVED )
+ return;
+ if ( tokenize )
+ text = null;
+ }
+
+ /**
+ * Parse an entity reference inside a tag or attribute.
+ *
+ * @throws XmlPullParserException if invalid XML is detected.
+ * @throws IOException if an I/O error is found.
+ */
+ private void parseEntityRef()
+ throws XmlPullParserException, IOException
+ {
+ final int len = parseCharOrPredefinedEntityRef();
+
+ posEnd--; // don't involve the final ';' from the entity in the search
+
+ if ( resolvedEntityRefCharBuf != BUF_NOT_RESOLVED ) {
+ return;
+ }
+
+ resolvedEntityRefCharBuf = lookuEntityReplacement( len );
+ if ( resolvedEntityRefCharBuf != BUF_NOT_RESOLVED )
+ {
+ return;
+ }
+ if ( tokenize )
+ text = null;
+ }
+
+ /**
+ * Check if the provided parameter is a valid Char. According to
+ * https://www.w3.org/TR/REC-xml/#NT-Char
*
* @param codePoint the numeric value to check
* @return true if it is a valid numeric character reference. False otherwise.
@@ -2883,8 +2938,6 @@ private static boolean isValidCodePoint( int codePoint )
}
private char[] lookuEntityReplacement( int entityNameLen )
- throws XmlPullParserException, IOException
-
{
if ( !allStringsInterned )
{
@@ -2919,7 +2972,7 @@ private char[] lookuEntityReplacement( int entityNameLen )
}
}
}
- return null;
+ return BUF_NOT_RESOLVED;
}
private void parseComment()
@@ -2935,7 +2988,7 @@ private void parseComment()
posStart = pos;
final int curLine = lineNumber;
- final int curColumn = columnNumber;
+ final int curColumn = columnNumber - 4;
try
{
final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
@@ -2977,7 +3030,7 @@ else if (isValidCodePoint( ch ))
}
else
{
- throw new XmlPullParserException( "Illegal character 0x" + Integer.toHexString(((int) ch)) + " found in comment", this, null );
+ throw new XmlPullParserException( "Illegal character 0x" + Integer.toHexString(ch) + " found in comment", this, null );
}
if ( normalizeIgnorableWS )
{
@@ -3056,7 +3109,7 @@ private boolean parsePI()
if ( tokenize )
posStart = pos;
final int curLine = lineNumber;
- final int curColumn = columnNumber;
+ final int curColumn = columnNumber - 2;
int piTargetStart = pos;
int piTargetEnd = -1;
final boolean normalizeIgnorableWS = tokenize && !roundtripSupported;
@@ -3105,6 +3158,10 @@ else if ( !seenInnerTag )
throw new XmlPullParserException( "processing instruction started on line " + curLine
+ " and column " + curColumn + " was not closed", this, null );
}
+ else
+ {
+ seenInnerTag = false;
+ }
}
else if ( ch == '<' )
{
@@ -3484,7 +3541,8 @@ else if ( ch == '>' && bracketLevel == 0 )
break;
else if ( ch == '&' )
{
- extractEntityRef();
+ extractEntityRefInDocDecl();
+ continue;
}
if ( normalizeIgnorableWS )
{
@@ -3536,6 +3594,19 @@ else if ( ch == '\n' )
}
posEnd = pos - 1;
+ text = null;
+ }
+
+ private void extractEntityRefInDocDecl()
+ throws XmlPullParserException, IOException
+ {
+ // extractEntityRef
+ posEnd = pos - 1;
+
+ int prevPosStart = posStart;
+ parseEntityRefInDocDecl();
+
+ posStart = prevPosStart;
}
private void extractEntityRef()
@@ -3559,9 +3630,9 @@ private void extractEntityRef()
}
// assert usePC == true;
- final char[] resolvedEntity = parseEntityRef();
+ parseEntityRef();
// check if replacement text can be resolved !!!
- if ( resolvedEntity == null )
+ if ( resolvedEntityRefCharBuf == BUF_NOT_RESOLVED )
{
if ( entityRefName == null )
{
@@ -3571,7 +3642,7 @@ private void extractEntityRef()
+ "'", this, null );
}
// write into PC replacement text - do merge for replacement text!!!!
- for ( char aResolvedEntity : resolvedEntity )
+ for ( char aResolvedEntity : resolvedEntityRefCharBuf )
{
if ( pcEnd >= pc.length )
{
@@ -3893,7 +3964,7 @@ private char more()
fillBuf();
// this return value should be ignored as it is used in epilog parsing ...
if ( reachedEnd )
- return (char) -1;
+ throw new EOFException( "no more data available" + getPositionDescription() );
}
final char ch = buf[pos++];
// line/columnNumber
diff --git a/src/main/java/org/codehaus/plexus/util/xml/pull/MXSerializer.java b/src/main/java/org/codehaus/plexus/util/xml/pull/MXSerializer.java
index cd1edc59..c69db3f4 100644
--- a/src/main/java/org/codehaus/plexus/util/xml/pull/MXSerializer.java
+++ b/src/main/java/org/codehaus/plexus/util/xml/pull/MXSerializer.java
@@ -460,15 +460,15 @@ public void startDocument( String encoding, Boolean standalone )
if ( encoding != null )
{
out.write( " encoding=" );
- out.write( attributeUseApostrophe ? '\'' : '"' );
+ out.write( apos );
out.write( encoding );
- out.write( attributeUseApostrophe ? '\'' : '"' );
+ out.write( apos );
// out.write('\'');
}
if ( standalone != null )
{
out.write( " standalone=" );
- out.write( attributeUseApostrophe ? '\'' : '"' );
+ out.write( apos );
if ( standalone )
{
out.write( "yes" );
@@ -477,7 +477,7 @@ public void startDocument( String encoding, Boolean standalone )
{
out.write( "no" );
}
- out.write( attributeUseApostrophe ? '\'' : '"' );
+ out.write( apos );
// if(standalone.booleanValue()) {
// out.write(" standalone='yes'");
// } else {
diff --git a/src/test/java/org/codehaus/plexus/util/SelectorUtilsTest.java b/src/test/java/org/codehaus/plexus/util/SelectorUtilsTest.java
index ebc2dca0..160aa16d 100644
--- a/src/test/java/org/codehaus/plexus/util/SelectorUtilsTest.java
+++ b/src/test/java/org/codehaus/plexus/util/SelectorUtilsTest.java
@@ -27,7 +27,6 @@
* SelectorUtilsTest class.
* * @author herve - * @version $Id: $Id * @since 3.4.0 */ public class SelectorUtilsTest @@ -92,4 +91,55 @@ public void testMatchPath_WindowsFileSeparator() // Pattern and target don't start with file separator assertTrue( SelectorUtils.matchPath( "*" + separator + "a.txt", "b" + separator + "a.txt", separator, false ) ); } + + @Test + public void testPatternMatchSingleWildcardPosix() + { + assertFalse(SelectorUtils.matchPath( + "/com/test/*", + "/com/test/test/hallo")); + } + + + @Test + public void testPatternMatchDoubleWildcardCaseInsensitivePosix() + { + assertTrue(SelectorUtils.matchPath( + "/com/test/**", + "/com/test/test/hallo")); + } + + + @Test + public void testPatternMatchDoubleWildcardPosix() + { + assertTrue(SelectorUtils.matchPath( + "/com/test/**", + "/com/test/test/hallo")); + } + + + @Test + public void testPatternMatchSingleWildcardWindows() + { + assertFalse(SelectorUtils.matchPath( + "D:\\com\\test\\*", + "D:\\com\\test\\test\\hallo")); + + assertFalse(SelectorUtils.matchPath( + "D:/com/test/*", + "D:/com/test/test/hallo")); + } + + @Test + public void testPatternMatchDoubleWildcardWindows() + { + assertTrue(SelectorUtils.matchPath( + "D:\\com\\test\\**", + "D:\\com\\test\\test\\hallo")); + + assertTrue(SelectorUtils.matchPath( + "D:\\com\\test\\**", + "D:/com/test/test/hallo")); + } } diff --git a/src/test/java/org/codehaus/plexus/util/io/CachingOutputStreamTest.java b/src/test/java/org/codehaus/plexus/util/io/CachingOutputStreamTest.java new file mode 100644 index 00000000..e7888ad4 --- /dev/null +++ b/src/test/java/org/codehaus/plexus/util/io/CachingOutputStreamTest.java @@ -0,0 +1,142 @@ +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.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileTime; +import java.util.Objects; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +public class CachingOutputStreamTest +{ + + Path tempDir; + Path checkLastModified; + + @Before + public void setup() throws IOException + { + Path dir = Paths.get( "target/io" ); + Files.createDirectories( dir ); + tempDir = Files.createTempDirectory( dir, "temp-" ); + checkLastModified = tempDir.resolve( ".check" ); + } + + private void waitLastModified() throws IOException, InterruptedException + { + Files.newOutputStream( checkLastModified ).close(); + FileTime lm = Files.getLastModifiedTime( checkLastModified ); + while ( true ) + { + Files.newOutputStream( checkLastModified ).close(); + FileTime nlm = Files.getLastModifiedTime( checkLastModified ); + if ( !Objects.equals( nlm, lm ) ) + { + break; + } + Thread.sleep( 10 ); + } + } + + @Test + public void testWriteNoExistingFile() throws IOException, InterruptedException + { + byte[] data = "Hello world!".getBytes( StandardCharsets.UTF_8 ); + Path path = tempDir.resolve( "file.txt" ); + assertFalse( Files.exists( path ) ); + + try ( CachingOutputStream cos = new CachingOutputStream( path, 4 ) ) + { + cos.write( data ); + } + assertTrue( Files.exists( path ) ); + byte[] read = Files.readAllBytes( path ); + assertArrayEquals( data, read ); + FileTime modified = Files.getLastModifiedTime( path ); + + waitLastModified(); + + try ( CachingOutputStream cos = new CachingOutputStream( path, 4 ) ) + { + cos.write( data ); + } + assertTrue( Files.exists( path ) ); + read = Files.readAllBytes( path ); + assertArrayEquals( data, read ); + FileTime newModified = Files.getLastModifiedTime( path ); + assertEquals( modified, newModified ); + modified = newModified; + + waitLastModified(); + + // write longer data + data = "Good morning!".getBytes( StandardCharsets.UTF_8 ); + try ( CachingOutputStream cos = new CachingOutputStream( path, 4 ) ) + { + cos.write( data ); + } + assertTrue( Files.exists( path ) ); + read = Files.readAllBytes( path ); + assertArrayEquals( data, read ); + newModified = Files.getLastModifiedTime( path ); + assertNotEquals( modified, newModified ); + modified = newModified; + + waitLastModified(); + + // different data same size + data = "Good mornong!".getBytes( StandardCharsets.UTF_8 ); + try ( CachingOutputStream cos = new CachingOutputStream( path, 4 ) ) + { + cos.write( data ); + } + assertTrue( Files.exists( path ) ); + read = Files.readAllBytes( path ); + assertArrayEquals( data, read ); + newModified = Files.getLastModifiedTime( path ); + assertNotEquals( modified, newModified ); + modified = newModified; + + waitLastModified(); + + // same data but shorter + data = "Good mornon".getBytes( StandardCharsets.UTF_8 ); + try ( CachingOutputStream cos = new CachingOutputStream( path, 4 ) ) + { + cos.write( data ); + } + assertTrue( Files.exists( path ) ); + read = Files.readAllBytes( path ); + assertArrayEquals( data, read ); + newModified = Files.getLastModifiedTime( path ); + assertNotEquals( modified, newModified ); + modified = newModified; + } + +} diff --git a/src/test/java/org/codehaus/plexus/util/io/CachingWriterTest.java b/src/test/java/org/codehaus/plexus/util/io/CachingWriterTest.java new file mode 100644 index 00000000..a4ffec91 --- /dev/null +++ b/src/test/java/org/codehaus/plexus/util/io/CachingWriterTest.java @@ -0,0 +1,140 @@ +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.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileTime; +import java.util.Objects; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +public class CachingWriterTest +{ + + Path tempDir; + Path checkLastModified; + + @Before + public void setup() throws IOException + { + Path dir = Paths.get( "target/io" ); + Files.createDirectories( dir ); + tempDir = Files.createTempDirectory( dir, "temp-" ); + checkLastModified = tempDir.resolve( ".check" ); + } + + private void waitLastModified() throws IOException, InterruptedException + { + Files.newOutputStream( checkLastModified ).close(); + FileTime lm = Files.getLastModifiedTime( checkLastModified ); + while ( true ) + { + Files.newOutputStream( checkLastModified ).close(); + FileTime nlm = Files.getLastModifiedTime( checkLastModified ); + if ( !Objects.equals( nlm, lm ) ) + { + break; + } + Thread.sleep( 10 ); + } + } + + @Test + public void testWriteNoExistingFile() throws IOException, InterruptedException + { + String data = "Hello world!"; + Path path = tempDir.resolve( "file.txt" ); + assertFalse( Files.exists( path ) ); + + try ( CachingWriter cos = new CachingWriter( path, StandardCharsets.UTF_8, 4 ) ) + { + cos.write( data ); + } + assertTrue( Files.exists( path ) ); + String read = new String( Files.readAllBytes( path ), StandardCharsets.UTF_8 ); + assertEquals( data, read ); + FileTime modified = Files.getLastModifiedTime( path ); + + waitLastModified(); + + try ( CachingWriter cos = new CachingWriter( path, StandardCharsets.UTF_8, 4 ) ) + { + cos.write( data ); + } + assertTrue( Files.exists( path ) ); + read = new String( Files.readAllBytes( path ), StandardCharsets.UTF_8 ); + assertEquals( data, read ); + FileTime newModified = Files.getLastModifiedTime( path ); + assertEquals( modified, newModified ); + modified = newModified; + + waitLastModified(); + + // write longer data + data = "Good morning!"; + try ( CachingWriter cos = new CachingWriter( path, StandardCharsets.UTF_8, 4 ) ) + { + cos.write( data ); + } + assertTrue( Files.exists( path ) ); + read = new String( Files.readAllBytes( path ), StandardCharsets.UTF_8 ); + assertEquals( data, read ); + newModified = Files.getLastModifiedTime( path ); + assertNotEquals( modified, newModified ); + modified = newModified; + + waitLastModified(); + + // different data same size + data = "Good mornong!"; + try ( CachingWriter cos = new CachingWriter( path, StandardCharsets.UTF_8, 4 ) ) + { + cos.write( data ); + } + assertTrue( Files.exists( path ) ); + read = new String( Files.readAllBytes( path ), StandardCharsets.UTF_8 ); + assertEquals( data, read ); + newModified = Files.getLastModifiedTime( path ); + assertNotEquals( modified, newModified ); + modified = newModified; + + waitLastModified(); + + // same data but shorter + data = "Good mornon"; + try ( CachingWriter cos = new CachingWriter( path, StandardCharsets.UTF_8, 4 ) ) + { + cos.write( data ); + } + assertTrue( Files.exists( path ) ); + read = new String( Files.readAllBytes( path ), StandardCharsets.UTF_8 ); + assertEquals( data, read ); + newModified = Files.getLastModifiedTime( path ); + assertNotEquals( modified, newModified ); + modified = newModified; + } +} diff --git a/src/test/java/org/codehaus/plexus/util/xml/pull/MXParserTest.java b/src/test/java/org/codehaus/plexus/util/xml/pull/MXParserTest.java index e0be666a..e5e04708 100644 --- a/src/test/java/org/codehaus/plexus/util/xml/pull/MXParserTest.java +++ b/src/test/java/org/codehaus/plexus/util/xml/pull/MXParserTest.java @@ -17,6 +17,7 @@ */ import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -29,6 +30,7 @@ import java.nio.file.Files; import java.nio.file.Paths; +import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.ReaderFactory; import org.junit.Test; @@ -351,6 +353,35 @@ public void testValidCharacterReferenceDecimal() * * @throws java.lang.Exception if any. */ + @Test + public void testParserPosition() + throws Exception + { + String input = " \ntest entity ref with entities appearing in tags, Unix line separator.
+ * + * Regression test: assure same behavior of MXParser from plexus-utils 3.3.0. + * + * @throws IOException if any. + * + * @since 3.4.2 + */ + @Test + public void testEntityRefTextUnix() + throws IOException + { + testEntityRefText( "\n" ); + } + + /** + *test entity ref with entities appearing in tags, DOS line separator.
+ * + * Regression test: assure same behavior of MXParser from plexus-utils 3.3.0. + * + * @throws IOException if any. + * + * @since 3.4.2 + */ + @Test + public void testEntityRefTextDOS() + throws IOException + { + testEntityRefText( "\r\n" ); + } + + private void testEntityRefText( String newLine ) + throws IOException + { + StringBuilder sb = new StringBuilder(); + sb.append( "" ).append( newLine ); + sb.append( "" ).append( newLine ); + sb.append( "" ).append( newLine ); + sb.append( "" ).append( newLine ); + sb.append( "]>" ).append( newLine ); + sb.append( "&foo;&foo1;&foo2;&tritPos;" ); + + try + { + MXParser parser = new MXParser(); + parser.setInput( new StringReader( sb.toString() ) ); + parser.defineEntityReplacementText( "foo", "ř" ); + parser.defineEntityReplacementText( "nbsp", " " ); + parser.defineEntityReplacementText( "foo1", " " ); + parser.defineEntityReplacementText( "foo2", "š" ); + parser.defineEntityReplacementText( "tritPos", "𝟭" ); + + assertEquals( XmlPullParser.DOCDECL, parser.nextToken() ); + assertEquals( " test [\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "]", parser.getText() ); + assertEquals( XmlPullParser.IGNORABLE_WHITESPACE, parser.nextToken() ); + assertEquals( XmlPullParser.START_TAG, parser.nextToken() ); + assertEquals( "b", parser.getName() ); + assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() ); + assertEquals( "ř", parser.getText() ); + assertEquals( "foo", parser.getName() ); + assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() ); + assertEquals( " ", parser.getText() ); + assertEquals( "foo1", parser.getName() ); + assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() ); + assertEquals( "š", parser.getText() ); + assertEquals( "foo2", parser.getName() ); + assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() ); + assertEquals( "𝟭", parser.getText() ); + assertEquals( "tritPos", parser.getName() ); + assertEquals( XmlPullParser.END_TAG, parser.nextToken() ); + assertEquals( "b", parser.getName() ); + assertEquals( XmlPullParser.END_DOCUMENT, parser.nextToken() ); + } + catch ( XmlPullParserException e ) + { + fail( "should not raise exception: " + e ); + } + } + + /** + * Ensures that entity ref getText() and getName() return the correct value. + * + * Regression test: assure same behavior of MXParser from plexus-utils 3.3.0. + * + * @throws IOException if any. + * + * @since 3.4.2 + */ + @Test + public void testEntityReplacement() throws IOException { + String input = "
&&foo;&tritPos;
", parser.getText() );
+ assertEquals( "p", parser.getName() );
+ assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+ assertEquals( "&", parser.getText() );
+ assertEquals( "amp", parser.getName() );
+ assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+ assertEquals( "ř", parser.getText() );
+ assertEquals( "foo", parser.getName() );
+ assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+ assertEquals( "𝟭", parser.getText() );
+ assertEquals( "tritPos", parser.getName() );
+ assertEquals( XmlPullParser.END_TAG, parser.nextToken() );
+ assertEquals( "p", parser.getName() );
+ assertEquals( XmlPullParser.END_TAG, parser.nextToken() );
+ assertEquals( "section", parser.getName() );
+ assertEquals( XmlPullParser.END_DOCUMENT, parser.nextToken() );
+ }
+ catch ( XmlPullParserException e )
+ {
+ fail( "should not raise exception: " + e );
+ }
+ }
}
diff --git a/src/test/resources/xml/test-entities-DOS.xml b/src/test/resources/xml/test-entities-DOS.xml
new file mode 100644
index 00000000..e1d6d17a
--- /dev/null
+++ b/src/test/resources/xml/test-entities-DOS.xml
@@ -0,0 +1,6 @@
+
+
+
+]>
+