/*****************************************************************************
* This file is part of Rinzo
*
* Author: Claudio Cancinos
* WWW: https://sourceforge.net/projects/editorxml
* Copyright (C): 2008, Claudio Cancinos
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; If not, see <http://www.gnu.org/licenses/>
****************************************************************************/
package ar.com.tadp.xml.rinzo.core.model.visitor;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import org.apache.commons.lang.StringUtils;
import ar.com.tadp.xml.rinzo.XMLEditorPlugin;
import ar.com.tadp.xml.rinzo.core.model.XMLAttribute;
import ar.com.tadp.xml.rinzo.core.model.XMLNode;
/**
* This visitor creates a formated String from a node and his children.
*
* Improvements:
* 1) If I have something like: <servlet-name>StripesDispatcher</servlet-name> it might not pass it to 3 lines.
* 2) Take into account the previously formated node because of the CR. Because tags following a comment tag should not add an extra CR.
* This is really messy if I have something like this:
* <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
* <!-- Configuration -->
* <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
*
* NOTE: Maybe is not such a good idea that the elements right inside the root element are separated by CR :-\
* 3) Think about comments tags. I should let them to be separated as they want from anything else in order to gain clarity in the xml
*
* @author ccancinos
*/
public class ToStringVisitor implements StringGeneratorVisitor {
private StringBuffer buffer = new StringBuffer();
private int indentation = 0;
private int addIndentation = 0;
private boolean addLineSeparator = true;
private String lineSeparator = System.getProperty("line.separator");
public boolean visitStart(XMLNode node) {
if (this.indentation > 0) {
this.addLineSeparator();
}
this.addIdentation();
this.addLine(node.getContent(), node);
this.indentation++;
if (!node.hasChildren() && node.getCorrespondingNode() == null) {
this.addLineSeparator = false;
}
return true;
}
public boolean visitEnd(XMLNode node) {
this.indentation--;
if (this.addLineSeparator) {
this.addLineSeparator();
this.addIdentation();
} else {
this.addLineSeparator = true;
}
this.addLine(node.getCorrespondingNode().getContent().trim(), node.getCorrespondingNode());
return true;
}
public boolean visitChild(XMLNode node) {
String content = node.getContent();
String trimmedContent = "";
if ((node.isTextTag()) || (node.isCdata()) || (node.isEmptyTag()) || (node.isCommentTag())) {
trimmedContent = content.trim();
if (StringUtils.isEmpty(trimmedContent)) {
this.addBlankLines(content);
}
}
if (trimmedContent.length() > 0) {
boolean singleChild = node.getParent().getChildren().size() == 1;
if (node.isCommentTag() || node.isEmptyTag() || trimmedContent.length() > this.getMaxLineLength()
|| !singleChild) {
this.addLineSeparator();
this.addIdentation();
} else {
if (singleChild) {
this.addLineSeparator = false;
}
}
this.addLine(trimmedContent, node);
}
return true;
}
private void addBlankLines(String content) {
int index = content.indexOf(lineSeparator);
index = content.indexOf(lineSeparator, index + 1);
while (index >= 0) {
this.addLineSeparator();
this.addIdentation();
index = content.indexOf(lineSeparator, index + 1);
}
}
private void addIdentation() {
for (int i = 0; i < indentation + this.addIndentation; i++) {
this.buffer.append(this.getIndentToken());
}
}
private void addLineSeparator() {
this.buffer.append(lineSeparator);
}
public void reset() {
this.buffer = new StringBuffer();
this.addIndentation = 0;
}
public String getString() {
return this.buffer.toString();
}
private void addLine(String line, XMLNode node) {
if (node.isSchemaRootTag() && !node.isEndTag()) {
this.addRootTag(node);
} else {
this.addSimpleLine(line, node);
}
}
private void addSimpleLine(String line, XMLNode node) {
if (line.length() <= this.getMaxLineLength()) {
this.buffer.append(line);
} else {
StringTokenizer tokenizer = new StringTokenizer(line, " \n\r\t");
StringBuffer currentLine = new StringBuffer();
StringBuffer finalLine = new StringBuffer();
while (tokenizer.hasMoreTokens()) {
while (tokenizer.hasMoreTokens() && currentLine.length() <= this.getMaxLineLength()) {
String nextToken = tokenizer.nextToken().trim();
currentLine.append((currentLine.toString().trim().length() == 0) ? nextToken : " " + nextToken);
}
if (currentLine.length() != 0) {
if (tokenizer.hasMoreTokens()) {
currentLine.append(lineSeparator);
}
finalLine.append(currentLine.toString());
currentLine = new StringBuffer();
int maxIndentation = indentation + this.addIndentation
+ ((node.isTextTag() || node.isCdata()) ? 0 : 1);
for (int i = 0; i < maxIndentation; i++) {
currentLine.append(this.getIndentToken());
}
}
}
this.buffer.append(finalLine.toString());
}
}
private void addRootTag(XMLNode node) {
this.buffer.append("<" + node.getFullTagName());
if (!node.getAttributes().isEmpty()) {
this.buffer.append(" ");
this.indentation++;
Map<String, XMLAttribute> attributes = node.getAttributes();
Set<String> keySet = new TreeSet<String>(attributes.keySet());
for (Iterator<String> iterator = keySet.iterator(); iterator.hasNext();) {
String string = iterator.next();
XMLAttribute attribute = attributes.get(string);
buffer.append(attribute.getName());
buffer.append("=\"");
this.addSimpleLine(attribute.getValue(), node);
buffer.append("\"");
if (iterator.hasNext()) {
buffer.append(this.lineSeparator);
this.addIdentation();
}
}
this.indentation--;
}
this.buffer.append(">");
}
private int getMaxLineLength() {
return XMLEditorPlugin.getMaximumLineWidth();
}
private String getIndentToken() {
return XMLEditorPlugin.getDefault().getIndentToken();
}
public void setAddIndentation(int addIndentation) {
this.addIndentation = addIndentation;
}
public void setLineSeparator(String lineSeparator) {
this.lineSeparator = lineSeparator;
}
}