PR: 18849 Submitted by: jan@materne.de (Jan Mat�rne) git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@274446 13f79535-47bb-0310-9956-ffa450edef68master
@@ -32,6 +32,9 @@ Changes that could break older environments: | |||||
Fixed bugs: | Fixed bugs: | ||||
----------- | ----------- | ||||
* A new attribute named skip is added to the TailFilter and | |||||
HeadFilter filter readers. | |||||
* Expand tasks did not behave as expected with PatternSets. | * Expand tasks did not behave as expected with PatternSets. | ||||
* <property environment=... /> now works on OS/400. | * <property environment=... /> now works on OS/400. | ||||
@@ -240,7 +240,14 @@ This filter reads the first few lines from the data supplied to it. | |||||
<TR> | <TR> | ||||
<TD vAlign=top>lines</TD> | <TD vAlign=top>lines</TD> | ||||
<TD vAlign=top align="center">Number of lines to be read. | <TD vAlign=top align="center">Number of lines to be read. | ||||
Defaults to "10"</TD> | |||||
Defaults to "10" <BR> A negative value means that all lines are | |||||
passed (useful with <I>skip</I>)</TD> | |||||
<TD vAlign=top align="center">No</TD> | |||||
</TR> | |||||
<TR> | |||||
<TD vAlign=top>skip</TD> | |||||
<TD vAlign=top align="center">Number of lines to be skipped (from the beginning). | |||||
Defaults to "0"</TD> | |||||
<TD vAlign=top align="center">No</TD> | <TD vAlign=top align="center">No</TD> | ||||
</TR> | </TR> | ||||
</TABLE> | </TABLE> | ||||
@@ -267,6 +274,19 @@ Convenience method: | |||||
</loadfile> | </loadfile> | ||||
</PRE></BLOCKQUOTE> | </PRE></BLOCKQUOTE> | ||||
This stores the first 15 lines, skipping the first 2 lines, of the supplied data | |||||
in the porperty ${src.file.head}. (Means: lines 3-17) | |||||
<BLOCKQUOTE><PRE> | |||||
<loadfile srcfile="${src.file}" property="${src.file.head}"> | |||||
<filterchain> | |||||
<headfilter lines="15" skip="2"/> | |||||
</filterchain> | |||||
</loadfile> | |||||
</PRE></BLOCKQUOTE> | |||||
See the testcases for more examples (<I>src\etc\testcases\filters\head-tail.xml</I> in the | |||||
source distribution). | |||||
<H3><a name="linecontains">LineContains</a></H3> | <H3><a name="linecontains">LineContains</a></H3> | ||||
This filter includes only those lines that contain all the user-specified | This filter includes only those lines that contain all the user-specified | ||||
@@ -602,11 +622,110 @@ This filter reads the last few lines from the data supplied to it. | |||||
<TR> | <TR> | ||||
<TD vAlign=top>lines</TD> | <TD vAlign=top>lines</TD> | ||||
<TD vAlign=top align="center">Number of lines to be read. | <TD vAlign=top align="center">Number of lines to be read. | ||||
Defaults to "10"</TD> | |||||
Defaults to "10" <BR> A negative value means that all lines are | |||||
passed (useful with <I>skip</I>)</TD> | |||||
<TD vAlign=top align="center">No</TD> | |||||
</TR> | |||||
<TR> | |||||
<TD vAlign=top>skip</TD> | |||||
<TD vAlign=top align="center">Number of lines to be skipped (from the end). | |||||
Defaults to "0" </TD> | |||||
<TD vAlign=top align="center">No</TD> | <TD vAlign=top align="center">No</TD> | ||||
</TR> | </TR> | ||||
</TABLE> | </TABLE> | ||||
<P> | <P> | ||||
<H4>Background:</H4> | |||||
With HeadFilter and TailFilter you can extract each part of a text file you want. | |||||
This graphic shows the dependencies: | |||||
<TABLE cellSpacing=0 cellPadding=2 border=1> | |||||
<TR> | |||||
<TH> Content </TH> | |||||
<TH></TH> | |||||
<TH></TH> | |||||
<TH></TH> | |||||
<TH> Filter </TH> | |||||
</TR> | |||||
<TR> | |||||
<TD> Line 1 </TD> | |||||
<TD rowspan="2" bgcolor="#C0C0C0"> </TD> | |||||
<TD rowspan="9" bgcolor="#FF00FF"> </TD> | |||||
<TD rowspan="4"> </TD> | |||||
<TD rowspan="11"> | |||||
<TABLE> | |||||
<TR> | |||||
<TD bgcolor="#C0C0C0"> </TD> | |||||
<TD><PRE><filterchain> | |||||
<headfilter lines="2"/> | |||||
</filterchain></PRE></TD> | |||||
</TR> | |||||
<TR> | |||||
<TD bgcolor="#FF00FF"> </TD> | |||||
<TD><PRE><filterchain> | |||||
<tailfilter lines="-1" skip="2"/> | |||||
</filterchain></PRE></TD> | |||||
</TR> | |||||
<TR> | |||||
<TD bgcolor="#008000"> </TD> | |||||
<TD><PRE><filterchain> | |||||
<headfilter lines="-1" skip="2"/> | |||||
</filterchain></PRE></TD> | |||||
</TR> | |||||
<TR> | |||||
<TD bgcolor="#0000FF"> </TD> | |||||
<TD><PRE><filterchain> | |||||
<headfilter lines="-1" skip="2"/> | |||||
<tailfilter lines="-1" skip="2"/> | |||||
</filterchain></PRE></TD> | |||||
</TR> | |||||
<TR> | |||||
<TD bgcolor="#00FF00"> </TD> | |||||
<TD><PRE><filterchain> | |||||
<tailfilter lines="2"/> | |||||
</filterchain></PRE></TD> | |||||
</TR> | |||||
</TABLE> | |||||
</TD> | |||||
</TR> | |||||
<TR> | |||||
<TD> Line 2 </TD> | |||||
</TR> | |||||
<TR> | |||||
<TD> Line 3 </TD> | |||||
<TD rowspan="9" bgcolor="#008000"> </TD> | |||||
</TR> | |||||
<TR> | |||||
<TD> Line 4 </TD> | |||||
</TR> | |||||
<TR> | |||||
<TD> Line 5 </TD> | |||||
<TD rowspan="3" bgcolor="#0000FF"> </TD> | |||||
</TR> | |||||
<TR> | |||||
<TD> Lines ... </TD> | |||||
</TR> | |||||
<TR> | |||||
<TD> Line 95 </TD> | |||||
</TR> | |||||
<TR> | |||||
<TD> Line 96 </TD> | |||||
<TD rowspan="4"> </TD> | |||||
</TR> | |||||
<TR> | |||||
<TD> Line 97 </TD> | |||||
</TR> | |||||
<TR> | |||||
<TD> Line 98 </TD> | |||||
<TD rowspan="2" bgcolor="#00FF00"> </TD> | |||||
</TR> | |||||
<TR> | |||||
<TD> Line 99 </TD> | |||||
</TR> | |||||
</TABLE> | |||||
<H4>Examples:</H4> | <H4>Examples:</H4> | ||||
This stores the last 15 lines of the supplied data in the property ${src.file.tail} | This stores the last 15 lines of the supplied data in the property ${src.file.tail} | ||||
@@ -655,6 +774,18 @@ Convenience method: | |||||
</loadfile> | </loadfile> | ||||
</PRE></BLOCKQUOTE> | </PRE></BLOCKQUOTE> | ||||
This stores the last 10 lines, skipping the last 2 lines, of the supplied data | |||||
in the porperty ${src.file.head}. (Means: if supplied data contains 60 lines, | |||||
lines 49-58 are extracted) | |||||
<BLOCKQUOTE><PRE> | |||||
<loadfile srcfile="${src.file}" property="${src.file.head}"> | |||||
<filterchain> | |||||
<tailfilter lines="10" skip="2"/> | |||||
</filterchain> | |||||
</loadfile> | |||||
</PRE></BLOCKQUOTE> | |||||
<HR> | <HR> | ||||
<P align=center>Copyright © 2002-2003 Apache Software Foundation. All rights | <P align=center>Copyright © 2002-2003 Apache Software Foundation. All rights | ||||
@@ -0,0 +1,10 @@ | |||||
Line 1 | |||||
Line 2 | |||||
Line 3 | |||||
Line 4 | |||||
Line 5 | |||||
Line 6 | |||||
Line 7 | |||||
Line 8 | |||||
Line 9 | |||||
Line 10 |
@@ -0,0 +1,58 @@ | |||||
Line 3 | |||||
Line 4 | |||||
Line 5 | |||||
Line 6 | |||||
Line 7 | |||||
Line 8 | |||||
Line 9 | |||||
Line 10 | |||||
Line 11 | |||||
Line 12 | |||||
Line 13 | |||||
Line 14 | |||||
Line 15 | |||||
Line 16 | |||||
Line 17 | |||||
Line 18 | |||||
Line 19 | |||||
Line 20 | |||||
Line 21 | |||||
Line 22 | |||||
Line 23 | |||||
Line 24 | |||||
Line 25 | |||||
Line 26 | |||||
Line 27 | |||||
Line 28 | |||||
Line 29 | |||||
Line 30 | |||||
Line 31 | |||||
Line 32 | |||||
Line 33 | |||||
Line 34 | |||||
Line 35 | |||||
Line 36 | |||||
Line 37 | |||||
Line 38 | |||||
Line 39 | |||||
Line 40 | |||||
Line 41 | |||||
Line 42 | |||||
Line 43 | |||||
Line 44 | |||||
Line 45 | |||||
Line 46 | |||||
Line 47 | |||||
Line 48 | |||||
Line 49 | |||||
Line 50 | |||||
Line 51 | |||||
Line 52 | |||||
Line 53 | |||||
Line 54 | |||||
Line 55 | |||||
Line 56 | |||||
Line 57 | |||||
Line 58 | |||||
Line 59 | |||||
Line 60 |
@@ -0,0 +1,2 @@ | |||||
Line 1 | |||||
Line 2 |
@@ -0,0 +1,2 @@ | |||||
Line 3 | |||||
Line 4 |
@@ -0,0 +1,10 @@ | |||||
Line 3 | |||||
Line 4 | |||||
Line 5 | |||||
Line 6 | |||||
Line 7 | |||||
Line 8 | |||||
Line 9 | |||||
Line 10 | |||||
Line 11 | |||||
Line 12 |
@@ -0,0 +1 @@ | |||||
Line 4 |
@@ -0,0 +1,10 @@ | |||||
Line 51 | |||||
Line 52 | |||||
Line 53 | |||||
Line 54 | |||||
Line 55 | |||||
Line 56 | |||||
Line 57 | |||||
Line 58 | |||||
Line 59 | |||||
Line 60 |
@@ -0,0 +1,58 @@ | |||||
Line 1 | |||||
Line 2 | |||||
Line 3 | |||||
Line 4 | |||||
Line 5 | |||||
Line 6 | |||||
Line 7 | |||||
Line 8 | |||||
Line 9 | |||||
Line 10 | |||||
Line 11 | |||||
Line 12 | |||||
Line 13 | |||||
Line 14 | |||||
Line 15 | |||||
Line 16 | |||||
Line 17 | |||||
Line 18 | |||||
Line 19 | |||||
Line 20 | |||||
Line 21 | |||||
Line 22 | |||||
Line 23 | |||||
Line 24 | |||||
Line 25 | |||||
Line 26 | |||||
Line 27 | |||||
Line 28 | |||||
Line 29 | |||||
Line 30 | |||||
Line 31 | |||||
Line 32 | |||||
Line 33 | |||||
Line 34 | |||||
Line 35 | |||||
Line 36 | |||||
Line 37 | |||||
Line 38 | |||||
Line 39 | |||||
Line 40 | |||||
Line 41 | |||||
Line 42 | |||||
Line 43 | |||||
Line 44 | |||||
Line 45 | |||||
Line 46 | |||||
Line 47 | |||||
Line 48 | |||||
Line 49 | |||||
Line 50 | |||||
Line 51 | |||||
Line 52 | |||||
Line 53 | |||||
Line 54 | |||||
Line 55 | |||||
Line 56 | |||||
Line 57 | |||||
Line 58 |
@@ -0,0 +1,2 @@ | |||||
Line 59 | |||||
Line 60 |
@@ -0,0 +1,2 @@ | |||||
Line 57 | |||||
Line 58 |
@@ -0,0 +1,10 @@ | |||||
Line 49 | |||||
Line 50 | |||||
Line 51 | |||||
Line 52 | |||||
Line 53 | |||||
Line 54 | |||||
Line 55 | |||||
Line 56 | |||||
Line 57 | |||||
Line 58 |
@@ -0,0 +1,107 @@ | |||||
<?xml version="1.0"?> | |||||
<project default="cleanup" basedir="."> | |||||
<target name="init"> | |||||
<mkdir dir="result" /> | |||||
</target> | |||||
<target name="cleanup"> | |||||
<delete dir="result"/> | |||||
</target> | |||||
<!-- Testcases for HeadFilter --> | |||||
<target name="testHead" depends="init"> | |||||
<copy file="input/head-tail.test" tofile="result/head-tail.head.test"> | |||||
<filterchain> | |||||
<headfilter/> | |||||
</filterchain> | |||||
</copy> | |||||
</target> | |||||
<target name="testHeadLines" depends="init"> | |||||
<copy file="input/head-tail.test" tofile="result/head-tail.headLines.test"> | |||||
<filterchain> | |||||
<headfilter lines="2"/> | |||||
</filterchain> | |||||
</copy> | |||||
</target> | |||||
<target name="testHeadSkip" depends="init"> | |||||
<copy file="input/head-tail.test" tofile="result/head-tail.headSkip.test"> | |||||
<filterchain> | |||||
<headfilter skip="2"/> | |||||
</filterchain> | |||||
</copy> | |||||
</target> | |||||
<target name="testHeadLinesSkip" depends="init"> | |||||
<copy file="input/head-tail.test" tofile="result/head-tail.headLinesSkip.test"> | |||||
<filterchain> | |||||
<headfilter lines="2" skip="2"/> | |||||
</filterchain> | |||||
</copy> | |||||
</target> | |||||
<target name="testHeadAllSkip" depends="init"> | |||||
<copy file="input/head-tail.test" tofile="result/head-tail.headAllSkip.test"> | |||||
<filterchain> | |||||
<headfilter lines="-1" skip="2"/> | |||||
</filterchain> | |||||
</copy> | |||||
</target> | |||||
<!-- Testcases for TailFilter --> | |||||
<target name="testTail" depends="init"> | |||||
<copy file="input/head-tail.test" tofile="result/head-tail.tail.test"> | |||||
<filterchain> | |||||
<tailfilter/> | |||||
</filterchain> | |||||
</copy> | |||||
</target> | |||||
<target name="testTailLines" depends="init"> | |||||
<copy file="input/head-tail.test" tofile="result/head-tail.tailLines.test"> | |||||
<filterchain> | |||||
<tailfilter lines="2"/> | |||||
</filterchain> | |||||
</copy> | |||||
</target> | |||||
<target name="testTailSkip" depends="init"> | |||||
<copy file="input/head-tail.test" tofile="result/head-tail.tailSkip.test"> | |||||
<filterchain> | |||||
<tailfilter skip="2"/> | |||||
</filterchain> | |||||
</copy> | |||||
</target> | |||||
<target name="testTailLinesSkip" depends="init"> | |||||
<copy file="input/head-tail.test" tofile="result/head-tail.tailLinesSkip.test"> | |||||
<filterchain> | |||||
<tailfilter lines="2" skip="2"/> | |||||
</filterchain> | |||||
</copy> | |||||
</target> | |||||
<target name="testTailAllSkip" depends="init"> | |||||
<copy file="input/head-tail.test" tofile="result/head-tail.tailAllSkip.test"> | |||||
<filterchain> | |||||
<tailfilter lines="-1" skip="2"/> | |||||
</filterchain> | |||||
</copy> | |||||
</target> | |||||
<!-- Testcases for combined scenarios --> | |||||
<target name="testHeadTail" depends="init"> | |||||
<copy file="input/head-tail.test" tofile="result/head-tail.headtail.test"> | |||||
<filterchain> | |||||
<headfilter lines="4"/> | |||||
<tailfilter lines="2"/> | |||||
</filterchain> | |||||
</copy> | |||||
</target> | |||||
</project> |
@@ -0,0 +1,5 @@ | |||||
Line 1 | |||||
Line 2 | |||||
Line 3 | |||||
Line 4 | |||||
Line 5 |
@@ -0,0 +1,60 @@ | |||||
Line 1 | |||||
Line 2 | |||||
Line 3 | |||||
Line 4 | |||||
Line 5 | |||||
Line 6 | |||||
Line 7 | |||||
Line 8 | |||||
Line 9 | |||||
Line 10 | |||||
Line 11 | |||||
Line 12 | |||||
Line 13 | |||||
Line 14 | |||||
Line 15 | |||||
Line 16 | |||||
Line 17 | |||||
Line 18 | |||||
Line 19 | |||||
Line 20 | |||||
Line 21 | |||||
Line 22 | |||||
Line 23 | |||||
Line 24 | |||||
Line 25 | |||||
Line 26 | |||||
Line 27 | |||||
Line 28 | |||||
Line 29 | |||||
Line 30 | |||||
Line 31 | |||||
Line 32 | |||||
Line 33 | |||||
Line 34 | |||||
Line 35 | |||||
Line 36 | |||||
Line 37 | |||||
Line 38 | |||||
Line 39 | |||||
Line 40 | |||||
Line 41 | |||||
Line 42 | |||||
Line 43 | |||||
Line 44 | |||||
Line 45 | |||||
Line 46 | |||||
Line 47 | |||||
Line 48 | |||||
Line 49 | |||||
Line 50 | |||||
Line 51 | |||||
Line 52 | |||||
Line 53 | |||||
Line 54 | |||||
Line 55 | |||||
Line 56 | |||||
Line 57 | |||||
Line 58 | |||||
Line 59 | |||||
Line 60 |
@@ -1,7 +1,7 @@ | |||||
/* | /* | ||||
* The Apache Software License, Version 1.1 | * The Apache Software License, Version 1.1 | ||||
* | * | ||||
* Copyright (c) 2002 The Apache Software Foundation. All rights | |||||
* Copyright (c) 2002-2003 The Apache Software Foundation. All rights | |||||
* reserved. | * reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
@@ -76,15 +76,21 @@ public final class HeadFilter | |||||
/** Parameter name for the number of lines to be returned. */ | /** Parameter name for the number of lines to be returned. */ | ||||
private static final String LINES_KEY = "lines"; | private static final String LINES_KEY = "lines"; | ||||
/** Parameter name for the number of lines to be skipped. */ | |||||
private static final String SKIP_KEY = "skip"; | |||||
/** Number of lines currently read in. */ | /** Number of lines currently read in. */ | ||||
private long linesRead = 0; | private long linesRead = 0; | ||||
/** Number of lines to be returned in the filtered stream. */ | /** Number of lines to be returned in the filtered stream. */ | ||||
private long lines = 10; | private long lines = 10; | ||||
/** Number of lines to be skipped. */ | |||||
private long skip = 0; | |||||
/** | /** | ||||
* Constructor for "dummy" instances. | * Constructor for "dummy" instances. | ||||
* | |||||
* | |||||
* @see BaseFilterReader#BaseFilterReader() | * @see BaseFilterReader#BaseFilterReader() | ||||
*/ | */ | ||||
public HeadFilter() { | public HeadFilter() { | ||||
@@ -104,14 +110,14 @@ public final class HeadFilter | |||||
/** | /** | ||||
* Returns the next character in the filtered stream. If the desired | * Returns the next character in the filtered stream. If the desired | ||||
* number of lines have already been read, the resulting stream is | * number of lines have already been read, the resulting stream is | ||||
* effectively at an end. Otherwise, the next character from the | |||||
* effectively at an end. Otherwise, the next character from the | |||||
* underlying stream is read and returned. | * underlying stream is read and returned. | ||||
* | |||||
* | |||||
* @return the next character in the resulting stream, or -1 | * @return the next character in the resulting stream, or -1 | ||||
* if the end of the resulting stream has been reached | * if the end of the resulting stream has been reached | ||||
* | |||||
* | |||||
* @exception IOException if the underlying stream throws an IOException | * @exception IOException if the underlying stream throws an IOException | ||||
* during reading | |||||
* during reading | |||||
*/ | */ | ||||
public final int read() throws IOException { | public final int read() throws IOException { | ||||
if (!getInitialized()) { | if (!getInitialized()) { | ||||
@@ -121,7 +127,13 @@ public final class HeadFilter | |||||
int ch = -1; | int ch = -1; | ||||
if (linesRead < lines) { | |||||
// skip the lines (if set) | |||||
while (skip > 0) { | |||||
for (int tmp = in.read(); tmp != '\n'; tmp = in.read()); | |||||
skip--; | |||||
} | |||||
if ( (linesRead < lines) || (lines < 0) ){ | |||||
ch = in.read(); | ch = in.read(); | ||||
@@ -135,7 +147,7 @@ public final class HeadFilter | |||||
/** | /** | ||||
* Sets the number of lines to be returned in the filtered stream. | * Sets the number of lines to be returned in the filtered stream. | ||||
* | |||||
* | |||||
* @param lines the number of lines to be returned in the filtered stream | * @param lines the number of lines to be returned in the filtered stream | ||||
*/ | */ | ||||
public final void setLines(final long lines) { | public final void setLines(final long lines) { | ||||
@@ -144,26 +156,45 @@ public final class HeadFilter | |||||
/** | /** | ||||
* Returns the number of lines to be returned in the filtered stream. | * Returns the number of lines to be returned in the filtered stream. | ||||
* | |||||
* | |||||
* @return the number of lines to be returned in the filtered stream | * @return the number of lines to be returned in the filtered stream | ||||
*/ | */ | ||||
private final long getLines() { | private final long getLines() { | ||||
return lines; | return lines; | ||||
} | } | ||||
/** | |||||
* Sets the number of lines to be skipped in the filtered stream. | |||||
* | |||||
* @param lines the number of lines to be skipped in the filtered stream | |||||
*/ | |||||
public final void setSkip(final long skip) { | |||||
this.skip = skip; | |||||
} | |||||
/** | |||||
* Returns the number of lines to be skipped in the filtered stream. | |||||
* | |||||
* @return the number of lines to be skipped in the filtered stream | |||||
*/ | |||||
private final long getSkip() { | |||||
return skip; | |||||
} | |||||
/** | /** | ||||
* Creates a new HeadFilter using the passed in | * Creates a new HeadFilter using the passed in | ||||
* Reader for instantiation. | * Reader for instantiation. | ||||
* | |||||
* | |||||
* @param rdr A Reader object providing the underlying stream. | * @param rdr A Reader object providing the underlying stream. | ||||
* Must not be <code>null</code>. | * Must not be <code>null</code>. | ||||
* | |||||
* | |||||
* @return a new filter based on this configuration, but filtering | * @return a new filter based on this configuration, but filtering | ||||
* the specified reader | * the specified reader | ||||
*/ | */ | ||||
public final Reader chain(final Reader rdr) { | public final Reader chain(final Reader rdr) { | ||||
HeadFilter newFilter = new HeadFilter(rdr); | HeadFilter newFilter = new HeadFilter(rdr); | ||||
newFilter.setLines(getLines()); | newFilter.setLines(getLines()); | ||||
newFilter.setSkip(getSkip()); | |||||
newFilter.setInitialized(true); | newFilter.setInitialized(true); | ||||
return newFilter; | return newFilter; | ||||
} | } | ||||
@@ -180,6 +211,10 @@ public final class HeadFilter | |||||
lines = new Long(params[i].getValue()).longValue(); | lines = new Long(params[i].getValue()).longValue(); | ||||
break; | break; | ||||
} | } | ||||
if (SKIP_KEY.equals(params[i].getName())) { | |||||
skip = new Long(params[i].getValue()).longValue(); | |||||
break; | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -1,7 +1,7 @@ | |||||
/* | /* | ||||
* The Apache Software License, Version 1.1 | * The Apache Software License, Version 1.1 | ||||
* | * | ||||
* Copyright (c) 2002 The Apache Software Foundation. All rights | |||||
* Copyright (c) 2002-2003 The Apache Software Foundation. All rights | |||||
* reserved. | * reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
@@ -78,12 +78,18 @@ public final class TailFilter | |||||
/** Parameter name for the number of lines to be returned. */ | /** Parameter name for the number of lines to be returned. */ | ||||
private static final String LINES_KEY = "lines"; | private static final String LINES_KEY = "lines"; | ||||
/** Parameter name for the number of lines to be skipped. */ | |||||
private static final String SKIP_KEY = "skip"; | |||||
/** Number of lines currently read in. */ | /** Number of lines currently read in. */ | ||||
private long linesRead = 0; | private long linesRead = 0; | ||||
/** Number of lines to be returned in the filtered stream. */ | /** Number of lines to be returned in the filtered stream. */ | ||||
private long lines = 10; | private long lines = 10; | ||||
/** Number of lines to be skipped. */ | |||||
private long skip = 0; | |||||
/** Buffer to hold in characters read ahead. */ | /** Buffer to hold in characters read ahead. */ | ||||
private char[] buffer = new char[4096]; | private char[] buffer = new char[4096]; | ||||
@@ -98,7 +104,7 @@ public final class TailFilter | |||||
/** | /** | ||||
* Constructor for "dummy" instances. | * Constructor for "dummy" instances. | ||||
* | |||||
* | |||||
* @see BaseFilterReader#BaseFilterReader() | * @see BaseFilterReader#BaseFilterReader() | ||||
*/ | */ | ||||
public TailFilter() { | public TailFilter() { | ||||
@@ -121,12 +127,12 @@ public final class TailFilter | |||||
* Otherwise, the stream is read to the end and buffered (with the buffer | * Otherwise, the stream is read to the end and buffered (with the buffer | ||||
* growing as necessary), then the appropriate position in the buffer is | * growing as necessary), then the appropriate position in the buffer is | ||||
* set to read from. | * set to read from. | ||||
* | |||||
* | |||||
* @return the next character in the resulting stream, or -1 | * @return the next character in the resulting stream, or -1 | ||||
* if the end of the resulting stream has been reached | * if the end of the resulting stream has been reached | ||||
* | |||||
* | |||||
* @exception IOException if the underlying stream throws an IOException | * @exception IOException if the underlying stream throws an IOException | ||||
* during reading | |||||
* during reading | |||||
*/ | */ | ||||
public final int read() throws IOException { | public final int read() throws IOException { | ||||
if (!getInitialized()) { | if (!getInitialized()) { | ||||
@@ -152,16 +158,18 @@ public final class TailFilter | |||||
} | } | ||||
} | } | ||||
if (ch == '\n' || ch == -1) { | |||||
++linesRead; | |||||
if (lines > 0) { | |||||
if (ch == '\n' || ch == -1) { | |||||
++linesRead; | |||||
if (linesRead == lines) { | |||||
int i = 0; | |||||
for (i = returnedCharPos + 1; | |||||
buffer[i] != 0 && buffer[i] != '\n'; i++) { | |||||
if ((linesRead == lines + skip)) { | |||||
int i = 0; | |||||
for (i = returnedCharPos + 1; | |||||
buffer[i] != 0 && buffer[i] != '\n'; i++) { | |||||
} | |||||
returnedCharPos = i; | |||||
--linesRead; | |||||
} | } | ||||
returnedCharPos = i; | |||||
--linesRead; | |||||
} | } | ||||
} | } | ||||
if (ch == -1) { | if (ch == -1) { | ||||
@@ -174,6 +182,26 @@ public final class TailFilter | |||||
completedReadAhead = true; | completedReadAhead = true; | ||||
} | } | ||||
// Because the complete stream is read into the buffer I can delete | |||||
// the "skip lines" from back to the beginning. | |||||
if (skip > 0) { | |||||
// searching... | |||||
int i; | |||||
for (i = buffer.length - 1; skip > 0; i--) { | |||||
if (buffer[i]=='\n') { | |||||
skip--; | |||||
} | |||||
} | |||||
// cut the buffer to the new length | |||||
char[] newBuffer = new char[i]; | |||||
System.arraycopy(buffer, 0, newBuffer, 0, i); | |||||
buffer = newBuffer; | |||||
// don´t forget to set the "lastposition" new | |||||
bufferPos = i; | |||||
} | |||||
++returnedCharPos; | ++returnedCharPos; | ||||
if (returnedCharPos >= bufferPos) { | if (returnedCharPos >= bufferPos) { | ||||
return -1; | return -1; | ||||
@@ -184,7 +212,7 @@ public final class TailFilter | |||||
/** | /** | ||||
* Sets the number of lines to be returned in the filtered stream. | * Sets the number of lines to be returned in the filtered stream. | ||||
* | |||||
* | |||||
* @param lines the number of lines to be returned in the filtered stream | * @param lines the number of lines to be returned in the filtered stream | ||||
*/ | */ | ||||
public final void setLines(final long lines) { | public final void setLines(final long lines) { | ||||
@@ -193,26 +221,45 @@ public final class TailFilter | |||||
/** | /** | ||||
* Returns the number of lines to be returned in the filtered stream. | * Returns the number of lines to be returned in the filtered stream. | ||||
* | |||||
* | |||||
* @return the number of lines to be returned in the filtered stream | * @return the number of lines to be returned in the filtered stream | ||||
*/ | */ | ||||
private final long getLines() { | private final long getLines() { | ||||
return lines; | return lines; | ||||
} | } | ||||
/** | |||||
* Sets the number of lines to be skipped in the filtered stream. | |||||
* | |||||
* @param lines the number of lines to be skipped in the filtered stream | |||||
*/ | |||||
public final void setSkip(final long skip) { | |||||
this.skip = skip; | |||||
} | |||||
/** | |||||
* Returns the number of lines to be skipped in the filtered stream. | |||||
* | |||||
* @return the number of lines to be skipped in the filtered stream | |||||
*/ | |||||
private final long getSkip() { | |||||
return skip; | |||||
} | |||||
/** | /** | ||||
* Creates a new TailFilter using the passed in | * Creates a new TailFilter using the passed in | ||||
* Reader for instantiation. | * Reader for instantiation. | ||||
* | |||||
* | |||||
* @param rdr A Reader object providing the underlying stream. | * @param rdr A Reader object providing the underlying stream. | ||||
* Must not be <code>null</code>. | * Must not be <code>null</code>. | ||||
* | |||||
* | |||||
* @return a new filter based on this configuration, but filtering | * @return a new filter based on this configuration, but filtering | ||||
* the specified reader | * the specified reader | ||||
*/ | */ | ||||
public final Reader chain(final Reader rdr) { | public final Reader chain(final Reader rdr) { | ||||
TailFilter newFilter = new TailFilter(rdr); | TailFilter newFilter = new TailFilter(rdr); | ||||
newFilter.setLines(getLines()); | newFilter.setLines(getLines()); | ||||
newFilter.setSkip(getSkip()); | |||||
newFilter.setInitialized(true); | newFilter.setInitialized(true); | ||||
return newFilter; | return newFilter; | ||||
} | } | ||||
@@ -229,6 +276,10 @@ public final class TailFilter | |||||
setLines(new Long(params[i].getValue()).longValue()); | setLines(new Long(params[i].getValue()).longValue()); | ||||
break; | break; | ||||
} | } | ||||
if (SKIP_KEY.equals(params[i].getName())) { | |||||
skip = new Long(params[i].getValue()).longValue(); | |||||
break; | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,155 @@ | |||||
/* | |||||
* The Apache Software License, Version 1.1 | |||||
* | |||||
* Copyright (c) 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 | |||||
* <http://www.apache.org/>. | |||||
*/ | |||||
package org.apache.tools.ant.filters; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import org.apache.tools.ant.BuildFileTest; | |||||
import org.apache.tools.ant.util.FileUtils; | |||||
/** JUnit Testcases for TailFilter and HeadFilter | |||||
* @author <a href="mailto:jan@materne.de">Jan Matèrne</a> | |||||
*/ | |||||
/* I wrote the testcases in one java file because I want also to test the | |||||
* combined behaviour (see end of the class). | |||||
*/ | |||||
public class HeadTailTest extends BuildFileTest { | |||||
public HeadTailTest(String name) { | |||||
super(name); | |||||
} | |||||
public void setUp() { | |||||
configureProject("src/etc/testcases/filters/head-tail.xml"); | |||||
} | |||||
public void tearDown() { | |||||
executeTarget("cleanup"); | |||||
} | |||||
public void testHead() throws IOException { | |||||
executeTarget("testHead"); | |||||
File expected = getProject().resolveFile("expected/head-tail.head.test"); | |||||
File result = getProject().resolveFile("result/head-tail.head.test"); | |||||
FileUtils fu = FileUtils.newFileUtils(); | |||||
assertTrue("testHead: Result not like expected", fu.contentEquals(expected, result)); | |||||
} | |||||
public void testHeadLines() throws IOException { | |||||
executeTarget("testHeadLines"); | |||||
File expected = getProject().resolveFile("expected/head-tail.headLines.test"); | |||||
File result = getProject().resolveFile("result/head-tail.headLines.test"); | |||||
FileUtils fu = FileUtils.newFileUtils(); | |||||
assertTrue("testHeadLines: Result not like expected", fu.contentEquals(expected, result)); | |||||
} | |||||
public void testHeadSkip() throws IOException { | |||||
executeTarget("testHeadSkip"); | |||||
File expected = getProject().resolveFile("expected/head-tail.headSkip.test"); | |||||
File result = getProject().resolveFile("result/head-tail.headSkip.test"); | |||||
FileUtils fu = FileUtils.newFileUtils(); | |||||
assertTrue("testHeadSkip: Result not like expected", fu.contentEquals(expected, result)); | |||||
} | |||||
public void testHeadLinesSkip() throws IOException { | |||||
executeTarget("testHeadLinesSkip"); | |||||
File expected = getProject().resolveFile("expected/head-tail.headLinesSkip.test"); | |||||
File result = getProject().resolveFile("result/head-tail.headLinesSkip.test"); | |||||
FileUtils fu = FileUtils.newFileUtils(); | |||||
assertTrue("testHeadLinesSkip: Result not like expected", fu.contentEquals(expected, result)); | |||||
} | |||||
public void testTail() throws IOException { | |||||
executeTarget("testTail"); | |||||
File expected = getProject().resolveFile("expected/head-tail.tail.test"); | |||||
File result = getProject().resolveFile("result/head-tail.tail.test"); | |||||
FileUtils fu = FileUtils.newFileUtils(); | |||||
assertTrue("testTail: Result not like expected", fu.contentEquals(expected, result)); | |||||
} | |||||
public void testTailLines() throws IOException { | |||||
executeTarget("testTailLines"); | |||||
File expected = getProject().resolveFile("expected/head-tail.tailLines.test"); | |||||
File result = getProject().resolveFile("result/head-tail.tailLines.test"); | |||||
FileUtils fu = FileUtils.newFileUtils(); | |||||
assertTrue("testTailLines: Result not like expected", fu.contentEquals(expected, result)); | |||||
} | |||||
public void testTailSkip() throws IOException { | |||||
executeTarget("testTailSkip"); | |||||
File expected = getProject().resolveFile("expected/head-tail.tailSkip.test"); | |||||
File result = getProject().resolveFile("result/head-tail.tailSkip.test"); | |||||
FileUtils fu = FileUtils.newFileUtils(); | |||||
assertTrue("testTailSkip: Result not like expected", fu.contentEquals(expected, result)); | |||||
} | |||||
public void testTailLinesSkip() throws IOException { | |||||
executeTarget("testTailLinesSkip"); | |||||
File expected = getProject().resolveFile("expected/head-tail.tailLinesSkip.test"); | |||||
File result = getProject().resolveFile("result/head-tail.tailLinesSkip.test"); | |||||
FileUtils fu = FileUtils.newFileUtils(); | |||||
assertTrue("testTailLinesSkip: Result not like expected", fu.contentEquals(expected, result)); | |||||
} | |||||
public void testHeadTail() throws IOException { | |||||
executeTarget("testHeadTail"); | |||||
File expected = getProject().resolveFile("expected/head-tail.headTail.test"); | |||||
File result = getProject().resolveFile("result/head-tail.headTail.test"); | |||||
FileUtils fu = FileUtils.newFileUtils(); | |||||
assertTrue("testHeadTail: Result not like expected", fu.contentEquals(expected, result)); | |||||
} | |||||
} |