/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.vdb.dynamic; import java.io.File; import java.io.FileInputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Properties; import java.util.Stack; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.teiid.core.designer.util.StringConstants; import org.teiid.designer.comments.Commentable; import org.teiid.designer.vdb.manifest.ConditionElement; import org.teiid.designer.vdb.manifest.DataRoleElement; import org.teiid.designer.vdb.manifest.EntryElement; import org.teiid.designer.vdb.manifest.ImportVdbElement; import org.teiid.designer.vdb.manifest.MaskElement; import org.teiid.designer.vdb.manifest.MetadataElement; import org.teiid.designer.vdb.manifest.ModelElement; import org.teiid.designer.vdb.manifest.PermissionElement; import org.teiid.designer.vdb.manifest.ProblemElement; import org.teiid.designer.vdb.manifest.PropertyElement; import org.teiid.designer.vdb.manifest.SourceElement; import org.teiid.designer.vdb.manifest.TranslatorElement; import org.teiid.designer.vdb.manifest.VdbElement; import org.teiid.designer.vdb.manifest.Visitor; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.ext.DefaultHandler2; /** * Process comments and append them to the {@link VdbElement} manifest */ public class CommentReader extends DefaultHandler2 implements Commentable, StringConstants { private static class ManifestVisitor implements Visitor { private MyElement context; /** * @param context */ public ManifestVisitor(MyElement context) { this.context = context; while (this.context.parent() != null) { this.context = this.context.parent(); } } private boolean checkContext(String elementType) { if (context == null) return false; return context.type().equals(elementType); } private String attribute(String attrName) { if (context == null || context.attributes() == null) return EMPTY_STRING; return context.attributes().getProperty(attrName); } private boolean nameAttribute(String name) { if (name == null) return false; return name.equals(attribute(NAME_ATTR)); } @Override public void visit(ConditionElement element) { if (!checkContext(CONDITION)) return; element.getComments().addCommentSet(EMPTY_STRING, context.comments()); } @Override public void visit(DataRoleElement drElement) { if (!checkContext(DATA_ROLE)) return; if (!nameAttribute(drElement.getName())) return; drElement.getComments().addCommentSet(EMPTY_STRING, context.comments()); MyElement drContext = this.context; for (MyElement child : drContext.children()) { this.context = child; if (checkContext(DESCRIPTION)) { drElement.getComments().addCommentSet(context.type(), context.comments()); continue; } if (checkContext(MAPPED_ROLE_NAME)) { drElement.getComments().addCommentSet(context.type() + HYPHEN + context.value(), context.comments()); continue; } if (checkContext(PERMISSION)) { for (PermissionElement pe : drElement.getPermissions()) pe.accept(this); } } this.context = drContext; } @Override public void visit(EntryElement element) { // Not Required } @Override public void visit(ImportVdbElement element) { if (!checkContext(IMPORT_VDB)) return; if (!nameAttribute(element.getName())) return; element.getComments().addCommentSet(EMPTY_STRING, context.comments()); } @Override public void visit(MaskElement element) { if (!checkContext(MASK)) return; element.getComments().addCommentSet(EMPTY_STRING, context.comments()); } @Override public void visit(MetadataElement element) { if (!checkContext(METADATA)) return; element.getComments().addCommentSet(EMPTY_STRING, context.comments()); } @Override public void visit(ModelElement modelElement) { if (!checkContext(MODEL)) return; if (!nameAttribute(modelElement.getName())) return; modelElement.getComments().addCommentSet(EMPTY_STRING, context.comments()); MyElement modelContext = this.context; for (MyElement child : modelContext.children()) { this.context = child; if (checkContext(DESCRIPTION)) { modelElement.getComments().addCommentSet(context.type(), context.comments()); continue; } if (checkContext(PROPERTY)) { for (PropertyElement pe : modelElement.getProperties()) pe.accept(this); continue; } if (checkContext(SOURCE)) { for (SourceElement se : modelElement.getSources()) se.accept(this); continue; } if (checkContext(METADATA)) { for (MetadataElement me : modelElement.getMetadata()) me.accept(this); continue; } } this.context = modelContext; } @Override public void visit(PermissionElement element) { if (!checkContext(PERMISSION)) return; String resourceName = null; MyElement permContext = this.context; for (MyElement child : context.children()) { this.context = child; if (checkContext(RESOURCE_NAME)) { resourceName = child.value(); break; } } this.context = permContext; if (!element.getResourceName().equals(resourceName)) return; element.getComments().addCommentSet(EMPTY_STRING, context.comments()); permContext = this.context; for (MyElement child : context.children()) { this.context = child; if (checkContext(RESOURCE_NAME)) { element.getComments().addCommentSet(RESOURCE_NAME, context.comments()); continue; } if (checkContext(MASK)) { element.getMask().accept(this); continue; } if (checkContext(CONDITION)) { element.getCondition().accept(this); continue; } if (checkContext(ALLOW_CREATE) || checkContext(ALLOW_READ) || checkContext(ALLOW_UPDATE) || checkContext(ALLOW_DELETE) || checkContext(ALLOW_EXECUTE) || checkContext(ALLOW_ALTER) || checkContext(ALLOW_LANGUAGE)) { element.getComments().addCommentSet(context.type(), context.comments()); } } this.context = permContext; } @Override public void visit(ProblemElement element) { // Not Required } @Override public void visit(PropertyElement element) { if (!checkContext(PROPERTY)) return; if (!nameAttribute(element.getName())) return; element.getComments().addCommentSet(EMPTY_STRING, context.comments()); } @Override public void visit(SourceElement element) { if (!checkContext(SOURCE)) return; if (!nameAttribute(element.getName())) return; element.getComments().addCommentSet(EMPTY_STRING, context.comments()); } @Override public void visit(TranslatorElement trElement) { if (!checkContext(TRANSLATOR)) return; if (!nameAttribute(trElement.getName())) return; trElement.getComments().addCommentSet(EMPTY_STRING, context.comments()); MyElement trContext = this.context; for (MyElement child : trContext.children()) { this.context = child; if (checkContext(PROPERTY)) { for (PropertyElement pe : trElement.getProperties()) pe.accept(this); } } this.context = trContext; } @Override public void visit(VdbElement vdbElement) { if (!checkContext(VDB)) return; vdbElement.getComments().addCommentSet(EMPTY_STRING, context.comments()); MyElement vdbContext = this.context; for (MyElement child : vdbContext.children()) { this.context = child; if (checkContext(DESCRIPTION) || checkContext(CONNECTION_TYPE)) { vdbElement.getComments().addCommentSet(context.type(), context.comments()); continue; } if (checkContext(PROPERTY)) { for (PropertyElement pe : vdbElement.getProperties()) pe.accept(this); continue; } if (checkContext(IMPORT_VDB)) { for (ImportVdbElement ive : vdbElement.getImportVdbEntries()) ive.accept(this); continue; } if (checkContext(MODEL)) { for (ModelElement me : vdbElement.getModels()) me.accept(this); continue; } if (checkContext(TRANSLATOR)) { for (TranslatorElement te : vdbElement.getTranslators()) te.accept(this); continue; } if (checkContext(DATA_ROLE)) { for (DataRoleElement dre : vdbElement.getDataPolicies()) dre.accept(this); continue; } } this.context = vdbContext; } } private static class MyElement { private String type; private String value; private int index; private Properties attributes = new Properties(); private List<String> comments; private MyElement parent; private List<MyElement> children; /** * @param type * @param parent */ public MyElement(String type, MyElement parent) { this.type = type; this.parent = parent; if (this.parent != null) parent.addChild(this); } public List<MyElement> children() { return Collections.unmodifiableList(children); } private void addChild(MyElement child) { if (children == null) children = new ArrayList<MyElement>(); children.add(child); int index = 0; for (MyElement kid : children) { if (kid == child) { kid.setIndex(index); break; // Should be at the end of the list anyway } if (kid.type().equals(child.type())) index++; } child.setParent(this); } /** * @return the parent */ public MyElement parent() { return this.parent; } /** * @param parent the parent to set */ private void setParent(MyElement parent) { this.parent = parent; } /** * @return type */ public String type() { return type; } /** * @return index */ public int index() { return index; } /** * @param index the index to set */ public void setIndex(int index) { this.index = index; } /** * @return the value */ public String value() { return this.value; } /** * @param value */ public void setValue(String value) { this.value = value; } /** * @return the attributes */ public Properties attributes() { return this.attributes; } /** * @param attributes */ public void setAttributes(Attributes attributes) { for (int i = 0; i < attributes.getLength(); ++i) { String name = attributes.getQName(i); String value = attributes.getValue(i); this.attributes.put(name, value); } } /** * @return the comments */ public List<String> comments() { if (this.comments == null) return Collections.emptyList(); return this.comments; } /** * @param comments */ public void setComments(List<String> comments) { this.comments = new ArrayList<String>(); this.comments.addAll(comments); } } private final VdbElement manifest; private Stack<String> comments = new Stack<String>(); private MyElement currentElement; /** * @param manifest */ public CommentReader(VdbElement manifest) { this.manifest = manifest; } @Override public void comment(char[] ch, int start, int length) throws SAXException { String value = new String(ch, start, length); if (value.length() == 0) return; // ignore white space comments.push(value); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { String elementType = (localName == null || localName.isEmpty()) ? qName : localName; currentElement = new MyElement(elementType, currentElement); currentElement.setAttributes(attributes); // New element encountered to scoop up the preceding comments currentElement.setComments(comments); comments.clear(); } @Override public void characters(char[] ch, int start, int length) throws SAXException { String value = new String(ch, start, length).trim(); currentElement.setValue(value); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (currentElement == null) return; if (currentElement.parent() != null) currentElement = currentElement.parent(); } /** * Process the given xml file and add comments to the manifest * * @param xmlFile * @throws Exception */ public void read(File xmlFile) throws Exception { FileInputStream xmlStream = null; try { SAXParserFactory factory = SAXParserFactory.newInstance(); xmlStream = new FileInputStream(xmlFile); SAXParser saxParser = factory.newSAXParser(); XMLReader xmlReader = saxParser.getXMLReader(); xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", this); //$NON-NLS-1$ saxParser.parse(xmlStream, this); // Done parsing and completed a tree of MyElements // Now visit the manifest using the tree as a context. ManifestVisitor visitor = new ManifestVisitor(currentElement); manifest.accept(visitor); } finally { if (xmlStream != null) xmlStream.close(); } } }