-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
489 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
305 changes: 305 additions & 0 deletions
305
jxmpp-core/src/main/java/org/jxmpp/xml/splitter/XmlPrettyPrinter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,305 @@ | |||
/** | |||
* | |||
* Copyright 2018 Florian Schmaus | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package org.jxmpp.xml.splitter; | |||
|
|||
import java.io.IOException; | |||
import java.io.Writer; | |||
|
|||
import org.jxmpp.xml.splitter.XmlSplitter.State; | |||
|
|||
public class XmlPrettyPrinter extends XmlPrinter { | |||
|
|||
private final int indent; | |||
private final int attributeIndent; | |||
private final int tabWidth; | |||
|
|||
private final PrettyPrintedXmlChunkCallback newChunkCallback; | |||
private final PrettyPrintedXmlPartCallback newPartCallback; | |||
private final Writer prettyWriter; | |||
|
|||
private StringBuilder currentPart; | |||
private StringBuilder currentChunk; | |||
|
|||
/** | |||
* Construct a new XML pretty printer. | |||
* | |||
* @param partCallback a part callback. | |||
*/ | |||
public XmlPrettyPrinter(PrettyPrintedXmlPartCallback partCallback) { | |||
this(builder().setPartCallback(partCallback)); | |||
} | |||
|
|||
/** | |||
* Construct a new XML pretty printer. | |||
* | |||
* @param prettyWriter a writer for the pretty printed XML stream. | |||
*/ | |||
public XmlPrettyPrinter(Writer prettyWriter) { | |||
this(builder().setPrettyWriter(prettyWriter)); | |||
} | |||
|
|||
private XmlPrettyPrinter(Builder builder) { | |||
this.indent = builder.indent; | |||
this.attributeIndent = builder.attributeIndent; | |||
this.tabWidth = builder.tabWidth; | |||
this.newChunkCallback = builder.newChunkCallback; | |||
this.newPartCallback = builder.newPartCallback; | |||
this.prettyWriter = builder.prettyWriter; | |||
} | |||
|
|||
@Override | |||
void onChunkStart() { | |||
if (newChunkCallback == null) { | |||
return; | |||
} | |||
currentChunk = new StringBuilder(currentPart.length() + 1024); | |||
currentChunk.append(currentPart); | |||
currentChunk.append('['); | |||
} | |||
|
|||
@Override | |||
void onChunkEnd() { | |||
if (newChunkCallback == null) { | |||
return; | |||
} | |||
currentChunk.append(']'); | |||
newChunkCallback.onPrettyPrintedXmlChunk(currentChunk); | |||
currentChunk = null; | |||
} | |||
|
|||
@SuppressWarnings("incomplete-switch") | |||
@Override | |||
void onNextChar(char c, int depth, State initialState, State currentState) throws IOException { | |||
final boolean stateChange = initialState != currentState; | |||
final StringBuilder sb = new StringBuilder(stateChange ? 16 : 1); | |||
|
|||
if (stateChange) { | |||
boolean deferredLeftAngle = false; | |||
int indent = 0; | |||
switch (currentState) { | |||
case TAG_LEFT_ANGLE_BRACKET: | |||
// Note that we return here because we need to see if this is a start tag or end tag. | |||
return; | |||
case END_TAG_SOLIDUS: | |||
indent = getElementIndent(depth - 1); | |||
deferredLeftAngle = true; | |||
break; | |||
case IN_TAG_NAME: | |||
indent = getElementIndent(depth); | |||
deferredLeftAngle = true; | |||
break; | |||
case IN_ATTRIBUTE_NAME: | |||
if (attributeIndent > 0) { | |||
indent = getAttributeIndent(depth); | |||
} | |||
break; | |||
case START: | |||
indent = getElementIndent(depth); | |||
break; | |||
} | |||
|
|||
if (indent > 0 || deferredLeftAngle) { | |||
sb.append('\n'); | |||
} | |||
|
|||
appendIndent(sb, indent); | |||
|
|||
if (deferredLeftAngle) { | |||
sb.append('<'); | |||
} | |||
} | |||
|
|||
sb.append(c); | |||
|
|||
if (currentChunk != null) { | |||
currentChunk.append(sb); | |||
} | |||
if (newPartCallback != null) { | |||
if (currentPart == null) { | |||
currentPart = new StringBuilder(1024); | |||
} | |||
currentPart.append(sb); | |||
} | |||
if (prettyWriter != null) { | |||
prettyWriter.append(sb); | |||
} | |||
} | |||
|
|||
@Override | |||
void onCompleteElement() { | |||
if (newPartCallback == null) { | |||
return; | |||
} | |||
if (currentPart.charAt(0) == '\n') { | |||
currentPart.deleteCharAt(0); | |||
} | |||
newPartCallback.onPrettyPrintedXmlPart(currentPart); | |||
currentPart = null; | |||
} | |||
|
|||
private int getElementIndent(int depth) { | |||
return indent * depth; | |||
} | |||
|
|||
private int getAttributeIndent(int depth) { | |||
return getElementIndent(depth) + attributeIndent; | |||
} | |||
|
|||
private void appendIndent(StringBuilder sb, int indent) { | |||
int spaces = indent; | |||
if (tabWidth > 0) { | |||
spaces = indent % tabWidth; | |||
int tabs = indent / tabWidth; | |||
for (int i = 0; i < tabs; i++) { | |||
sb.append('\t'); | |||
} | |||
} | |||
for (int i = 0; i < spaces; i++) { | |||
sb.append(' '); | |||
} | |||
} | |||
|
|||
public interface PrettyPrintedXmlChunkCallback { | |||
|
|||
/** | |||
* Invoked after the XML pretty printer handled a chunk. The pretty printed chunk will contain the current part | |||
* and the newly handled chunk enclosing in '[' and ']'. | |||
* | |||
* @param chunk the state of the current part with the newly handled chunk marked. | |||
*/ | |||
void onPrettyPrintedXmlChunk(StringBuilder chunk); | |||
} | |||
|
|||
public interface PrettyPrintedXmlPartCallback { | |||
|
|||
/** | |||
* Invoked after a part was completed. | |||
* | |||
* @param part the pertty printed part. | |||
*/ | |||
void onPrettyPrintedXmlPart(StringBuilder part); | |||
} | |||
|
|||
/** | |||
* Create a new builder. | |||
* | |||
* @return a new builder. | |||
*/ | |||
public static Builder builder() { | |||
return new Builder(); | |||
} | |||
|
|||
public static final class Builder { | |||
private int indent = 2; | |||
private int attributeIndent; | |||
private int tabWidth; | |||
|
|||
private PrettyPrintedXmlChunkCallback newChunkCallback; | |||
private PrettyPrintedXmlPartCallback newPartCallback; | |||
private Writer prettyWriter; | |||
|
|||
private Builder() { | |||
} | |||
|
|||
/** | |||
* Set the indent for elements in whitespace characters. | |||
* | |||
* @param indent the indent for elements in whitespace characters. | |||
* @return a reference to this builders. | |||
*/ | |||
public Builder setIndent(int indent) { | |||
ensureNotNegative(indent); | |||
|
|||
this.indent = indent; | |||
return this; | |||
} | |||
|
|||
/** | |||
* Set the attribute indent in whitespace characters. Use a value smaller one to disable attribute indentation. | |||
* | |||
* @param attributeIndent the attribute indent in whitespace characters. | |||
* @return a reference to this builder. | |||
*/ | |||
public Builder setAttributeIndent(int attributeIndent) { | |||
ensureNotNegative(attributeIndent); | |||
|
|||
this.attributeIndent = attributeIndent; | |||
return this; | |||
} | |||
|
|||
/** | |||
* Set the tab width in whitespace characters. Use a value smaller one to disable pretty printing with tabs. | |||
* | |||
* @param tabWidth the tab width in whitespace characters. | |||
* @return a reference to this builder. | |||
*/ | |||
public Builder setTabWidth(int tabWidth) { | |||
ensureNotNegative(tabWidth); | |||
|
|||
this.tabWidth = tabWidth; | |||
return this; | |||
} | |||
|
|||
/** | |||
* Set a chunk callback. | |||
* | |||
* @param chunkCallback the cunk callback. | |||
* @return a reference to this builder. | |||
*/ | |||
public Builder setChunkCallback(PrettyPrintedXmlChunkCallback chunkCallback) { | |||
this.newChunkCallback = chunkCallback; | |||
return this; | |||
} | |||
|
|||
/** | |||
* Set a part callback. | |||
* | |||
* @param partCallback the part callback. | |||
* @return a reference to this builder. | |||
*/ | |||
public Builder setPartCallback(PrettyPrintedXmlPartCallback partCallback) { | |||
this.newPartCallback = partCallback; | |||
return this; | |||
} | |||
|
|||
/** | |||
* Set a {@link Writer} for the pretty printed XML stream. | |||
* | |||
* @param prettyWriter the writer to pretty print to. | |||
* @return a reference to this builder. | |||
*/ | |||
public Builder setPrettyWriter(Writer prettyWriter) { | |||
this.prettyWriter = prettyWriter; | |||
return this; | |||
} | |||
|
|||
/** | |||
* Build an XML pretty printer. | |||
* | |||
* @return the newly build XML pretty printer. | |||
*/ | |||
public XmlPrettyPrinter build() { | |||
return new XmlPrettyPrinter(this); | |||
} | |||
|
|||
private static void ensureNotNegative(int i) { | |||
if (i < 0) { | |||
throw new IllegalArgumentException(); | |||
} | |||
} | |||
} | |||
} |
32 changes: 32 additions & 0 deletions
32
jxmpp-core/src/main/java/org/jxmpp/xml/splitter/XmlPrinter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,32 @@ | |||
/** | |||
* | |||
* Copyright 2018 Florian Schmaus | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package org.jxmpp.xml.splitter; | |||
|
|||
import java.io.IOException; | |||
|
|||
import org.jxmpp.xml.splitter.XmlSplitter.State; | |||
|
|||
public abstract class XmlPrinter { | |||
|
|||
abstract void onChunkStart(); | |||
|
|||
abstract void onChunkEnd(); | |||
|
|||
abstract void onNextChar(char c, int depth, State initialState, State currentState) throws IOException; | |||
|
|||
abstract void onCompleteElement(); | |||
} |
Oops, something went wrong.