Skip to content

Commit dd0ca9b

Browse files
committed
Fixed filter implementation to allow filename only matches (also fixed non-deterministic unit tests)
1 parent 4b1ad9f commit dd0ca9b

File tree

5 files changed

+83
-45
lines changed

5 files changed

+83
-45
lines changed

src/com/locima/xml2csv/configuration/filter/FileNameInputFilter.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,22 @@
1515
public class FileNameInputFilter extends FilterContainer {
1616

1717
private static final Logger LOG = LoggerFactory.getLogger(FileNameInputFilter.class);
18+
private boolean matchLocalFileNameOnly;
1819
private Pattern pattern;
1920

2021
/**
2122
* Sets the regex that this filter will use to match on {@link #include(File)}.
2223
*
2324
* @param regex the regular expression that will be used to match. Must not be null.
25+
* @param matchLocalFileNameOnly if true, then only the relative filename (i.e. no directory information) will be passed to the filter.
2426
* @throws PatternSyntaxException if the regular expression passed by <code>regex</code> is invalid.
2527
*/
2628
// CHECKSTYLE:OFF I don't care if PatternSyntaxException is a runtime exception, it's pertinent!
27-
public FileNameInputFilter(String regex) throws PatternSyntaxException {
29+
public FileNameInputFilter(String regex, boolean matchLocalFileNameOnly) throws PatternSyntaxException {
2830
// CHECKSTYLE:ON
2931
LOG.debug("Compiling regex {}", regex);
3032
this.pattern = Pattern.compile(regex);
33+
this.matchLocalFileNameOnly = matchLocalFileNameOnly;
3134
}
3235

3336
/**
@@ -39,16 +42,18 @@ public FileNameInputFilter(String regex) throws PatternSyntaxException {
3942
@Override
4043
public boolean include(File xmlInputFile) {
4144
boolean match;
42-
String absPath = xmlInputFile.getAbsolutePath();
45+
String pathToTest = this.matchLocalFileNameOnly ? xmlInputFile.getName() : xmlInputFile.getAbsolutePath();
4346
if (this.pattern == null) {
4447
LOG.warn("Regex pattern not specified on FileNameInputFilter, returning true");
4548
match = false;
4649
} else {
47-
match = this.pattern.matcher(absPath).find();
48-
LOG.debug("Input file {} did {}match file name input filter {}", absPath, match ? "" : "not ", this.pattern);
50+
match = this.pattern.matcher(pathToTest).find();
51+
LOG.debug("Input file {} did {}match file name input filter {}", pathToTest, match ? "" : "not ", this.pattern);
4952
if (match) {
5053
match = this.executeNestedFilters(xmlInputFile);
51-
LOG.trace("Input file {} excluded by nested filter", absPath);
54+
if (!match) {
55+
LOG.trace("Nested filter of {} rejected {}", this, xmlInputFile);
56+
}
5257
}
5358
}
5459
return match;

src/com/locima/xml2csv/inputparser/xml/ConfigContentHandler.java

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.regex.PatternSyntaxException;
88

99
import javax.xml.XMLConstants;
10+
import javax.xml.bind.DatatypeConverter;
1011

1112
import org.slf4j.Logger;
1213
import org.slf4j.LoggerFactory;
@@ -51,6 +52,7 @@ private static enum ElementNames {
5152

5253
private static final String CUSTOM_NAME_FORMAT_ATTR = "customNameFormat";
5354

55+
private static final String FILENAME_FILTER_MATCH_LOCAL_ATTR = "matchLocalFileNameOnly";
5456
private static final String GROUP_NUMBER_ATTR = "group";
5557
private static final String KEY_XPATH_ATTR = "keyXPath";
5658
private static final String KVPAIR_ROOT_XPATH_ATTR = "kvPairRoot";
@@ -322,13 +324,38 @@ private void endMappingList() {
322324
}
323325
}
324326

327+
/**
328+
* Retrieve an attribute value cast as a Boolean, according the rules specified by XSD spec ({@link DatatypeConverter#parseBoolean(String)}).
329+
*
330+
* @param atts the set of of attributes to retrieve from.
331+
* @param attrName the name of the attribute to retrieve.
332+
* @return true or false, depending on the value of the attribute, as per the XSD spec.
333+
* @throws SAXException if the value specified by <code>attrName</code> is present but not valid according to the XSD spec for XSD boolean value
334+
* types.
335+
*/
336+
private boolean getAttributeValueAsBoolean(Attributes atts, String attrName) throws SAXException {
337+
String attrValueAsString = atts.getValue(attrName);
338+
if (attrValueAsString == null) {
339+
return false;
340+
} else {
341+
try {
342+
boolean parsedBoolean = DatatypeConverter.parseBoolean(attrValueAsString);
343+
LOG.trace("Parsed {} value {} as {}", attrName, attrValueAsString, parsedBoolean);
344+
return parsedBoolean;
345+
} catch (IllegalArgumentException iae) {
346+
// Thrown by DatatypeConverter.parseBoolean for an invalid argument
347+
throw getException(iae, "Invalid value \"%s\" found in attriute \"%s\"", attrValueAsString, attrName);
348+
}
349+
}
350+
}
351+
325352
/**
326353
* Retrieve an attribute value cast as an Integer, null if one wasn't specified, or throw an exception if malformed.
327354
*
328355
* @param atts the set of of attributes to retrieve from.
329356
* @param attrName the name of the attribute to retrieve.
330-
* @return the value retrieved, or value in <code>defaultValue</code> if a value could not be found.
331-
* @throws SAXException thrown if the value found in the attribute is an integer.
357+
* @return an integer value of the value in <code>attrName</code>, null if no value was specified.
358+
* @throws SAXException thrown if the value found in the attribute is not an integer.
332359
*/
333360
private Integer getAttributeValueAsInteger(Attributes atts, String attrName) throws SAXException {
334361
String attrValueAsString = atts.getValue(attrName);
@@ -343,27 +370,6 @@ private Integer getAttributeValueAsInteger(Attributes atts, String attrName) thr
343370
}
344371
}
345372

346-
/**
347-
* Gets a list of the known child names of the parent container. These are used as variable names, available in the XPath evaluation of subsequent
348-
* mappings with the same parent.
349-
* @return an array, possibly empty, of known siblings of the current mapping (as determined by the top of {@link #mappingListStack}.
350-
*/
351-
private String[] getPreviousSiblingNames() {
352-
List<String> variables = new ArrayList<String>();
353-
MappingList currentContainer = this.mappingListStack.peek();
354-
for (IMapping m : currentContainer) {
355-
if (m instanceof IValueMapping) {
356-
IValueMapping vm = (IValueMapping) m;
357-
variables.add(vm.getName());
358-
}
359-
}
360-
String[] varArray = variables.toArray(new String[0]);
361-
if (LOG.isDebugEnabled()) {
362-
LOG.debug("Created variable set for {}: {}", currentContainer.getName(), StringUtil.toStringList(varArray));
363-
}
364-
return varArray;
365-
}
366-
367373
/**
368374
* Given an element's local name (namespace URI checking is left to the caller), return an enum value that can be used in switch statements to
369375
* branch logic depending on the input.
@@ -393,9 +399,8 @@ private ElementNames getElementNameEnum(String localName) throws SAXException {
393399
*/
394400
private SAXException getException(Exception inner, String message, Object... parameters) {
395401
String temp = String.format(message, parameters);
396-
XMLException de =
397-
new XMLException(inner, "Error parsing %s(%d:%d) %s", this.documentLocator.getSystemId(),
398-
this.documentLocator.getLineNumber(), this.documentLocator.getColumnNumber(), temp);
402+
XMLException de = new XMLException(inner, "Error parsing %s(%d:%d) %s", this.documentLocator.getSystemId(),
403+
this.documentLocator.getLineNumber(), this.documentLocator.getColumnNumber(), temp);
399404
SAXException se = new SAXException(de);
400405
return se;
401406
}
@@ -409,6 +414,28 @@ public MappingConfiguration getMappings() {
409414
return this.mappingConfiguration;
410415
}
411416

417+
/**
418+
* Gets a list of the known child names of the parent container. These are used as variable names, available in the XPath evaluation of subsequent
419+
* mappings with the same parent.
420+
*
421+
* @return an array, possibly empty, of known siblings of the current mapping (as determined by the top of {@link #mappingListStack}.
422+
*/
423+
private String[] getPreviousSiblingNames() {
424+
List<String> variables = new ArrayList<String>();
425+
MappingList currentContainer = this.mappingListStack.peek();
426+
for (IMapping m : currentContainer) {
427+
if (m instanceof IValueMapping) {
428+
IValueMapping vm = (IValueMapping) m;
429+
variables.add(vm.getName());
430+
}
431+
}
432+
String[] varArray = variables.toArray(new String[0]);
433+
if (LOG.isDebugEnabled()) {
434+
LOG.debug("Created variable set for {}: {}", currentContainer.getName(), StringUtil.toStringList(varArray));
435+
}
436+
return varArray;
437+
}
438+
412439
@Override
413440
public void setDocumentLocator(Locator locator) {
414441
this.documentLocator = locator;
@@ -466,7 +493,7 @@ public void startElement(String uri, String localName, String qName, Attributes
466493
startXPathFilter(atts.getValue("xPath"));
467494
break;
468495
case FileNameInputFilter:
469-
startFileNameFilter(atts.getValue("fileNameRegex"));
496+
startFileNameFilter(atts.getValue("fileNameRegex"), getAttributeValueAsBoolean(atts, FILENAME_FILTER_MATCH_LOCAL_ATTR));
470497
break;
471498
default:
472499
LOG.warn("Ignoring element ({}):{} as it isn't supported in this version of xml2csv", uri, localName);
@@ -480,11 +507,13 @@ public void startElement(String uri, String localName, String qName, Attributes
480507
* Adds filters to the mapping configuration.
481508
*
482509
* @param fileNameRegex a regular expression to match against the filename. May be null.
510+
* @param matchLocalFileNameOnly if false then the filter must match the absolute path name, if true then only the file name (no directory
511+
* information) will be used.
483512
* @throws SAXException If any errors occur whilst adding the filters.
484513
*/
485-
private void startFileNameFilter(String fileNameRegex) throws SAXException {
514+
private void startFileNameFilter(String fileNameRegex, boolean matchLocalFileNameOnly) throws SAXException {
486515
try {
487-
IInputFilter filter = new FileNameInputFilter(fileNameRegex);
516+
IInputFilter filter = new FileNameInputFilter(fileNameRegex, matchLocalFileNameOnly);
488517
addFilter(filter);
489518
} catch (PatternSyntaxException pse) {
490519
throw getException(pse, "Invalid Regular Expression {} specified for input filter.", fileNameRegex);

src/com/locima/xml2csv/inputparser/xml/MappingConfiguration.xsd

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,13 @@
203203
</documentation>
204204
</annotation>
205205
</attribute>
206+
<attribute name="matchLocalFileNameOnly" type="boolean" use="optional">
207+
<annotation>
208+
<documentation>
209+
Enter true to match on only the file name (exclude all directory information), false or omitted means that the absolute file name is used.
210+
</documentation>
211+
</annotation>
212+
</attribute>
206213
</complexType>
207214

208215
<complexType name="XPathInputFilter">

testsrc/com/locima/xml2csv/configuration/filter/FilterTests.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ public class FilterTests {
2323
private void checkAllInputFileFilterInputs(IInputFilter filter, boolean expectedResult, String... filenames) {
2424
for (String filename : filenames) {
2525
File f = new File(filename);
26-
assertEquals(expectedResult, filter.include(f));
26+
boolean result = filter.include(f);
27+
assertEquals("Test for " + f +" failed",expectedResult, result);
2728
}
2829
}
2930

@@ -35,18 +36,18 @@ private void checkAllXmlInputs(IInputFilter filter, boolean expectedResult, XdmN
3536

3637
@Test
3738
public void fileNameTest() {
38-
FileNameInputFilter filter = new FileNameInputFilter("\\.xml$");
39+
FileNameInputFilter filter = new FileNameInputFilter("\\.xml$",true);
3940
checkAllInputFileFilterInputs(filter, true, "wibble.xml", "test.xml", ".xml");
4041
checkAllInputFileFilterInputs(filter, false, "wibble.xml ", "wibble.xml.bak", "wibble.bak");
4142
}
4243

4344
@Test
4445
public void nestedTest() {
45-
FileNameInputFilter xmlFilter = new FileNameInputFilter("\\.xml$");
46-
FileNameInputFilter andyFilter = new FileNameInputFilter("a");
47-
xmlFilter.addNestedFilter(andyFilter);
48-
FileNameInputFilter tomFilter = new FileNameInputFilter("b");
49-
andyFilter.addNestedFilter(tomFilter);
46+
FileNameInputFilter xmlFilter = new FileNameInputFilter("\\.xml$", true);
47+
FileNameInputFilter aFilter = new FileNameInputFilter("a", true);
48+
xmlFilter.addNestedFilter(aFilter);
49+
FileNameInputFilter bFilter = new FileNameInputFilter("b", true);
50+
aFilter.addNestedFilter(bFilter);
5051

5152
checkAllInputFileFilterInputs(xmlFilter, false, "a.xml", "b.xml", "c.xml", "ab.bak", "ab.xml2");
5253
checkAllInputFileFilterInputs(xmlFilter, true, "ba.xml", "ab.xml", "abba.xml", "ab.xml.bak.xml");

testsrc/com/locima/xml2csv/output/OutputManagerTests.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package com.locima.xml2csv.output;
22

3-
import static org.junit.Assert.assertEquals;
4-
import static org.junit.Assert.assertNull;
5-
63
import java.io.File;
74
import java.io.IOException;
85
import java.util.Arrays;
@@ -25,7 +22,6 @@
2522
import com.locima.xml2csv.configuration.MappingList;
2623
import com.locima.xml2csv.configuration.MultiValueBehaviour;
2724
import com.locima.xml2csv.configuration.NameFormat;
28-
import com.locima.xml2csv.util.StringUtil;
2925
import com.locima.xml2csv.util.XmlUtil;
3026

3127
public class OutputManagerTests {

0 commit comments

Comments
 (0)