Skip to content

Commit

Permalink
Use Document Transformer to create XML
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthew Peterson committed Apr 18, 2024
1 parent f1e35ff commit 0b02d62
Show file tree
Hide file tree
Showing 11 changed files with 582 additions and 202 deletions.
21 changes: 14 additions & 7 deletions warehouse/age-off-utils/pom.xml
Expand Up @@ -28,13 +28,6 @@
</exclusion>
</exclusions>
</dependency>
<!-- For JAXBElement -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>${version.jakarta}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.accumulo</groupId>
<artifactId>accumulo-core</artifactId>
Expand All @@ -46,6 +39,20 @@
<artifactId>slf4j-api</artifactId>
<scope>compile</scope>
</dependency>
<!-- RuleConfigDocument -->
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>${version.xerces}</version>
<scope>compile</scope>
</dependency>
<!-- RuleConfigDocument -->
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.4.01</version>
<scope>compile</scope>
</dependency>
<!-- Test jar for TestFilter, etc. -->
<dependency>
<groupId>gov.nsa.datawave</groupId>
Expand Down
Expand Up @@ -2,7 +2,8 @@

import java.util.ArrayList;

import javax.xml.bind.JAXBElement;
import org.apache.xerces.dom.DocumentImpl;
import org.w3c.dom.Element;

import datawave.iterators.filter.ageoff.FilterRule;

Expand All @@ -15,15 +16,15 @@ public class AgeOffRuleConfiguration {
private String indentation = DEFAULT_INDENTATION;
private String ttlDuration;
private String ttlUnits;
private ArrayList<JAXBElement<?>> customElements;
private ArrayList<Element> customElements;

private AgeOffRuleConfiguration() {}

public String getIndentation() {
return indentation;
}

public ArrayList<JAXBElement<?>> getCustomElements() {
public ArrayList<Element> getCustomElements() {
return customElements;
}

Expand Down Expand Up @@ -93,11 +94,18 @@ public AgeOffRuleConfiguration build() {
return result;
}

public void addCustomElement(JAXBElement<?> customElement) {
public Builder addSimpleElement(String elementName, String textContent) {
Element element = new DocumentImpl().createElement(elementName);
element.setTextContent(textContent);
return addCustomElement(element);
}

public Builder addCustomElement(Element customElement) {
if (result.customElements == null) {
result.customElements = new ArrayList<>();
}
result.customElements.add(customElement);
return this;
}
}
}
Expand Up @@ -3,18 +3,22 @@
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.annotations.VisibleForTesting;

import datawave.ingest.util.cache.watch.AgeOffRuleLoader;

/**
* Formats a rule
*/
Expand All @@ -23,7 +27,7 @@ public class AgeOffRuleFormatter {

private final AgeOffRuleConfiguration configuration;
private final String indent;
private Writer writer;
private static int index = 0;

public AgeOffRuleFormatter(AgeOffRuleConfiguration configuration) {
this.configuration = configuration;
Expand All @@ -40,105 +44,76 @@ public AgeOffRuleFormatter(AgeOffRuleConfiguration configuration) {
*/
@VisibleForTesting
void format(Writer writer) throws IOException {
this.writer = writer;
openRuleElement();
writeFilterClass();
writeTtl();
writeMergeElement();
writeMatchPattern();
writeCustomElements();
closeRuleElement();
}

private void openRuleElement() throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("<rule");

String ruleLabel = this.configuration.getRuleLabel();
if (null != ruleLabel) {
log.debug("Adding label attribute to rule: {}", ruleLabel);
sb.append(" label=\"").append(ruleLabel).append("\"");
}
AgeOffRuleLoader.RuleConfig ruleConfig = createRuleConfig(this.configuration);

if (this.configuration.shouldMerge()) {
log.debug("Adding merge attribute to rule");
sb.append(" mode=\"merge\"");
}
sb.append(">\n");

this.writer.write(sb.toString());
writer.write(transformToXmlString(ruleConfig));
writer.close();
}

private void writeFilterClass() throws IOException {
this.writer.write(indent + "<filterClass>" + this.configuration.getFilterClass().getName() + "</filterClass>\n");
private AgeOffRuleLoader.RuleConfig createRuleConfig(AgeOffRuleConfiguration configuration) throws IOException {
AgeOffRuleLoader.RuleConfig ruleConfig = new AgeOffRuleLoader.RuleConfig(this.configuration.getFilterClass().getName(), index++);
ruleConfig.label(configuration.getRuleLabel());
ruleConfig.setIsMerge(this.configuration.shouldMerge());
ruleConfig.ttlValue(this.configuration.getTtlDuration());
ruleConfig.ttlUnits(this.configuration.getTtlUnits());
ruleConfig.matchPattern(buildMatchPattern());
ruleConfig.customElements(this.configuration.getCustomElements());
return ruleConfig;
}

private void writeTtl() throws IOException {
String duration = this.configuration.getTtlDuration();
private String transformToXmlString(AgeOffRuleLoader.RuleConfig ruleConfig) throws IOException {
try {
Transformer trans = initializeXmlTransformer();

if (duration != null) {
String units = configuration.getTtlUnits();
log.debug("Writing ttl for duration {} and units {}", duration, units);
this.writer.write(indent + "<ttl units=\"" + units + "\">" + duration + "</ttl>\n");
Writer writer = new StringWriter();

StreamResult result = new StreamResult(writer);
DOMSource source = new DOMSource(new RuleConfigDocument(ruleConfig));
trans.transform(source, result);

return writer.toString();
} catch (TransformerException e) {
throw new IOException("Failed to transform to XML", e);
}
}

private void writeMergeElement() throws IOException {
if (this.configuration.shouldMerge()) {
log.debug("Writing ismerge element");
this.writer.write(configuration.getIndentation() + "<ismerge>true</ismerge>\n");
}
private Transformer initializeXmlTransformer() throws TransformerConfigurationException {
Transformer trans = TransformerFactory.newInstance().newTransformer();
trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
trans.setOutputProperty(OutputKeys.METHOD, "xml");
trans.setOutputProperty(OutputKeys.INDENT, "yes");
trans.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", calculateIndentAmount());
return trans;
}

private void writeMatchPattern() throws IOException {
private String calculateIndentAmount() {
int length = configuration.getIndentation().length();
// add another four for every tab
length += (int) (4 * configuration.getIndentation().chars().filter(character -> character == '\t').count());
return Integer.toString(length);
}

private String buildMatchPattern() throws IOException {
if (configuration.getPatternConfiguration() == null) {
return;
return "";
}

log.debug("Writing match pattern");

this.writer.write(indent + "<matchPattern>\n");

StringWriter writer = new StringWriter();
AgeOffCsvToMatchPatternFormatter patternFormatter = new AgeOffCsvToMatchPatternFormatter(configuration.getPatternConfiguration());

// add two indentations: one for items under the rule element and another for items under the matchPattern element
String extraIndentation = this.indent + this.indent;
patternFormatter.write(new IndentingDelegatingWriter(extraIndentation, this.writer));
patternFormatter.write(new IndentingDelegatingWriter(extraIndentation, writer));

this.writer.write(indent + "</matchPattern>\n");
}

private void writeCustomElements() throws IOException {
ArrayList<JAXBElement<?>> customElements = this.configuration.getCustomElements();

if (null == customElements) {
return;
}

for (JAXBElement<?> customElement : customElements) {
writeCustomElement(customElement);
}
}

private void writeCustomElement(JAXBElement<?> customElement) throws IOException {
StringWriter tempWriter = new StringWriter();
marshalObject(customElement, tempWriter);
log.debug("Marshalled custom object to String: {}", tempWriter);
this.writer.write(indent + tempWriter + "\n");
}
String result = writer.toString();

private void marshalObject(JAXBElement<?> obj, Writer writer) throws IOException {
try {
JAXBContext jc = JAXBContext.newInstance(AnyXmlElement.class, obj.getClass());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.marshal(obj, writer);
} catch (JAXBException e) {
throw new IOException(e);
// final indentation to precede the closing of matchPattern
if (result.endsWith("\n")) {
return result + this.indent;
}
}

private void closeRuleElement() throws IOException {
this.writer.write("</rule>\n");
return result;
}
}

This file was deleted.

Expand Up @@ -6,20 +6,29 @@
public class IndentingDelegatingWriter extends Writer {
private final Writer writer;
private final String indentation;
private boolean shouldIndentNextWrite;

public IndentingDelegatingWriter(String indentation, Writer writer) {
this.indentation = indentation;
this.writer = writer;
this.shouldIndentNextWrite = true;
}

@Override
public void write(String line) throws IOException {
this.writer.write(indentation + line);
}
if (this.shouldIndentNextWrite) {
this.writer.write(indentation);
this.shouldIndentNextWrite = false;
}

@Override
public void write(char[] cbuf, int off, int len) throws IOException {
throw new UnsupportedOperationException();
String indentedLine = line.replaceAll("\n", "\n" + indentation);

// withhold indentation until later
if (indentedLine.endsWith("\n" + indentation)) {
indentedLine = indentedLine.substring(0, indentedLine.length() - indentation.length());
shouldIndentNextWrite = true;
}
this.writer.write(indentedLine);
}

@Override
Expand All @@ -31,4 +40,39 @@ public void flush() throws IOException {
public void close() throws IOException {
this.writer.close();
}

@Override
public void write(char[] cbuf, int off, int len) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public void write(int c) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public void write(char[] cbuf) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public void write(String str, int off, int len) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public Writer append(CharSequence csq) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public Writer append(CharSequence csq, int start, int end) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public Writer append(char c) throws IOException {
throw new UnsupportedOperationException();
}
}

0 comments on commit 0b02d62

Please sign in to comment.