/** * Copyright 2004-2016 Riccardo Solmi. All rights reserved. * This file is part of the Whole Platform. * * The Whole Platform 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 3 of the License, or * (at your option) any later version. * * The Whole Platform 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>. */ package org.whole.lang.xml.visitors; import org.whole.lang.matchers.Matcher; import org.whole.lang.model.IEntity; import org.whole.lang.model.adapters.IEntityAdapter; import org.whole.lang.operations.IPrettyPrintWriter; import org.whole.lang.operations.PrettyPrinterOperation; import org.whole.lang.util.EntityUtils; import org.whole.lang.util.StringUtils; import org.whole.lang.xml.model.Attribute; import org.whole.lang.xml.model.Attributes; import org.whole.lang.xml.model.CDataSect; import org.whole.lang.xml.model.CDataSectData; import org.whole.lang.xml.model.CharData; import org.whole.lang.xml.model.Comment; import org.whole.lang.xml.model.CommentText; import org.whole.lang.xml.model.Content; import org.whole.lang.xml.model.DocTypeDecl; import org.whole.lang.xml.model.Document; import org.whole.lang.xml.model.Element; import org.whole.lang.xml.model.Encoding; import org.whole.lang.xml.model.IContent; import org.whole.lang.xml.model.Instruction; import org.whole.lang.xml.model.Misc; import org.whole.lang.xml.model.Name; import org.whole.lang.xml.model.NameSpace; import org.whole.lang.xml.model.PI; import org.whole.lang.xml.model.Prolog; import org.whole.lang.xml.model.PubidLiteral; import org.whole.lang.xml.model.PublicId; import org.whole.lang.xml.model.QualifiedName; import org.whole.lang.xml.model.Standalone; import org.whole.lang.xml.model.SystemId; import org.whole.lang.xml.model.SystemLiteral; import org.whole.lang.xml.model.Value; import org.whole.lang.xml.model.Version; import org.whole.lang.xml.model.XMLDecl; import org.whole.lang.xml.reflect.XmlEntityDescriptorEnum; /** * @author Riccardo Solmi */ public class XmlPrettyPrinterVisitor extends XmlIdentityVisitor { protected final IPrettyPrintWriter out; public XmlPrettyPrinterVisitor(PrettyPrinterOperation operation) { out = operation.getPrettyPrintWriter(); } public boolean visitAdapter(IEntityAdapter entity) { if (!EntityUtils.isResolver(entity)) stagedVisit(entity.wGetAdaptee(false)); return false; } public void visit(Document entity) { entity.getProlog().accept(this); entity.getElement().accept(this); out.println(); } public void visit(Prolog entity) { entity.getXmlDecl().accept(this); entity.getDocTypeDecl().accept(this); entity.getMisc().accept(this); } public void visit(XMLDecl entity) { out.printRaw("<?xml"); entity.getVersion().accept(this); entity.getEncoding().accept(this); entity.getStandalone().accept(this); out.printlnRaw("?>"); } public void visit(Version entity) { out.printRaw(" version=\""); out.printRaw(entity.wStringValue()); out.printRaw("\""); } public void visit(Encoding entity) { out.printRaw(" encoding=\""); out.printRaw(entity.wStringValue()); out.printRaw("\""); } @Override public void visit(Standalone entity) { out.printRaw(" standalone=\"" + (entity.wBooleanValue() ? "yes" : "no") + "\""); } public void visit(DocTypeDecl entity) { if (EntityUtils.isResolver(entity.getName())) return; out.printRaw("<!DOCTYPE "); entity.getName().accept(this); out.printRaw(" "); entity.getExternalId().accept(this); out.printlnRaw(">"); } public void visit(SystemLiteral entity) { out.printRaw(entity.wStringValue()); } public void visit(SystemId entity) { out.printRaw("SYSTEM \""); entity.getSystemLiteral().accept(this); out.printRaw("\""); } public void visit(PubidLiteral entity) { out.printRaw(entity.wStringValue()); } public void visit(PublicId entity) { out.printRaw("PUBLIC \""); entity.getPubidLiteral().accept(this); out.printRaw("\" \""); entity.getSystemLiteral().accept(this); out.printRaw("\""); } public void visit(Misc entity) { for (int i = 0; i < entity.size(); i++) { entity.get(i).accept(this); out.println(); } } public void visit(Comment entity) { out.printRaw("<!--"); entity.getText().accept(this); out.printRaw("-->"); } public void visit(CommentText entity) { out.printRaw(StringUtils.toXMLCommentText(entity.getValue())); } public void visit(Instruction entity) { out.printRaw(entity.wStringValue()); } public void visit(PI entity) { out.printRaw("<?"); entity.getName().accept(this); out.printRaw(" "); entity.getInstruction().accept(this); out.printRaw("?>"); } public void visit(Attributes entity) { for (int i = 0; i < entity.size(); i++) entity.get(i).accept(this); } public void visit(Content entity) { IEntity prevChild = null; for (int i = 0; i < entity.size(); i++) { IContent child = entity.get(i); if (prevChild != null && !Matcher.match(XmlEntityDescriptorEnum.CharData, prevChild) && !Matcher.match(XmlEntityDescriptorEnum.CharData, child)) { out.println(); } child.accept(this); prevChild = child; } } public void visit(Element entity) { out.printRaw("<"); entity.getTag().accept(this); entity.getAttributes().accept(this); IContent content = entity.getContent(); int contentSize = content.wSize(); final int NO_CONTENT = 0; final int INLINE_CONTENT = 1; final int NESTED_CONTENT = 2; int contentCase = NESTED_CONTENT; if (Matcher.match(XmlEntityDescriptorEnum.Content, content)) { if (EntityUtils.isResolver(content)) contentCase = NO_CONTENT; else { switch (contentSize) { case 0: contentCase = NO_CONTENT; break; case 1: IEntity contentChild = content.wGet(0); if (EntityUtils.isResolver(contentChild)) contentCase = NO_CONTENT; else if (!Matcher.match(XmlEntityDescriptorEnum.Element, contentChild)) contentCase = INLINE_CONTENT; break; } } } else { contentCase = INLINE_CONTENT; } switch (contentCase) { case NO_CONTENT: out.printRaw("/>"); break; case INLINE_CONTENT: out.printRaw(">"); out.setInlined(true); content.accept(this); out.setInlined(false); out.printRaw("</"); entity.getTag().accept(this); out.printRaw(">"); break; case NESTED_CONTENT: out.printRaw(">"); out.setRelativeIndentation(+1); if (!Matcher.matchImpl(XmlEntityDescriptorEnum.CharData, content.wGet(0))) { out.println(); } content.accept(this); out.setRelativeIndentation(-1); if (!Matcher.matchImpl(XmlEntityDescriptorEnum.CharData, content.wGet(contentSize-1))) { out.println(); } out.printRaw("</"); entity.getTag().accept(this); out.printRaw(">"); break; } } public void visit(CharData entity) { out.printRaw(StringUtils.toXMLCharData(entity.wStringValue())); } public void visit(CDataSectData entity) { out.printRaw(StringUtils.toXMLCData(entity.wStringValue())); } public void visit(CDataSect entity) { out.printRaw("<![CDATA["); for (int i = 0; i < entity.size(); i++) entity.get(i).accept(this); out.printRaw("]]>"); } public void visit(Value entity) { out.printRaw(StringUtils.toXMLString(entity.wStringValue())); } public void visit(Attribute entity) { out.printRaw(" "); entity.getName().accept(this); out.printRaw("=\""); entity.getValue().accept(this); out.printRaw("\""); } public void visit(Name entity) { out.printRaw(entity.wStringValue()); } public void visit(NameSpace entity) { out.printRaw(entity.wStringValue()); } public void visit(QualifiedName entity) { if (Matcher.matchImpl(XmlEntityDescriptorEnum.NameSpace, entity.getNameSpace())) { String prefix = entity.getNameSpace().wStringValue(); if (prefix.length() > 0) { out.printRaw(prefix); out.printRaw(":"); } } entity.getName().accept(this); } }