package xid; import java.util.*; import java.io.*; import javax.xml.parsers.*; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import org.xml.sax.*; import org.w3c.dom.*; /** * */ public class Presenter { static protected org.apache.log4j.Logger log; static { log = org.apache.log4j.Logger.getLogger (Presenter.class.getName ()); } protected String webappPath; protected String sourceFileName; protected long sourceFileTime; protected Document doc; /* * */ public Presenter () { this.webappPath = null; this.sourceFileName = null; this.sourceFileTime = 0; this.doc = null; } /* * */ public Presenter (String webappPath, String fileName) { this.webappPath = webappPath; this.sourceFileName = fileName; this.sourceFileTime = 0; this.doc = null; } /* * */ public String getWebappPath () { String result; result = this.webappPath; // return (result); } /* * */ public void setSource (String fileName) { this.sourceFileName = fileName; this.sourceFileTime = 0; this.doc = null; } /* * */ public String getSource () { String result; result = this.sourceFileName; // return (result); } /* * */ public StringBuffer doXid (TagsDataById datas, StringBuffer errorOutput) { StringBuffer result; String sourceFilePath = this.webappPath + File.separator + this.sourceFileName; // Get the good tree. File source = new File (sourceFilePath); if (source == null) { String errorMessage = "source file not defined"; errorOutput.append (errorMessage); log.error (errorMessage); result = null; } else if (!source.exists ()) { String errorMessage = "source file defined but not found (" + sourceFilePath + ")"; errorOutput.append (errorMessage); log.error (errorMessage); result = null; } else if ((this.doc == null) || (this.sourceFileTime != source.lastModified ())) { this.sourceFileTime = source.lastModified (); this.doc = Presenter.fileToTree (sourceFilePath, errorOutput); if (this.doc != null) { Presenter.addMetaTag (doc, "generator", "XID 0.0"); } } // Build the web page. result = Presenter.doXid (doc, datas, this.webappPath, errorOutput); // return (result); } /* * Xid a file without data. */ static public StringBuffer doXid (String fileName, String webappPath, StringBuffer errorOutput) { StringBuffer result; Document doc = Presenter.fileToTree (fileName, errorOutput); if (doc == null) { result = null; } else { Presenter.addMetaTag (doc, "generator", "XID 0.0"); result = Presenter.doXid (doc, null, webappPath, errorOutput); } // return (result); } /* * Xid a string with html in. */ static public StringBuffer doXid (String html, TagsDataById datas, String webappPath, StringBuffer errorOutput) { StringBuffer result; String htmlSource; if ((html.startsWith ("")) || (html.startsWith (""))) { htmlSource = html; } else { htmlSource = "\n" + html + ""; } Document doc = null; // StringBufferInputStream is deprecated so we use another solution. // (see http://www.developpez.net/forums/archive/index.php/t-14101.html). doc = buildTree (new ByteArrayInputStream (htmlSource.getBytes ()), errorOutput); StringBuffer htmlTarget; htmlTarget = Presenter.process (doc, datas, webappPath, errorOutput); if (htmlTarget == null) { result = null; } else if ((html.startsWith ("")) || (html.startsWith (""))) { result = htmlTarget; } else { String bodyContent = extractBodyContent (htmlTarget); if (bodyContent == null) { result = null; } else { result = new StringBuffer (bodyContent); } } // return (result); } /* * Xid a file with data. */ static public StringBuffer doXid (Document doc, TagsDataById datas, String webappPath, StringBuffer errorOutput) { StringBuffer result; result = Presenter.process (doc, datas, webappPath, errorOutput); // return (result); } /* * */ static public String getClassAttributeValue (Node node) { String result; NamedNodeMap attributes = node.getAttributes (); if (attributes == null) { result = null; } else { Node nameAttribute = attributes.getNamedItem ("class"); if (nameAttribute == null) { result = null; } else { result = nameAttribute.getNodeValue (); } } // return (result); } /* * */ static protected StringBuffer processChildren (Node node, TagsDataById datas, String webappPath, StringBuffer errorOutput) { StringBuffer result; result = new StringBuffer (); NodeList children = node.getChildNodes(); if (children == null) { result.append (" "); } else { int childrenCount = children.getLength (); for (int i = 0; i < childrenCount; i++) { result.append (process (children.item(i), datas, webappPath, errorOutput)); } } // return (result); } /** * Includes another HSP file into the current page. * * @param node * @param attrMap * @param idAttr */ static protected StringBuffer processObjectTag (Node node, NamedNodeMap attrMap, Node idAttr, TagsDataById datas, String webappPath, StringBuffer errorOutput) { StringBuffer result; result = new StringBuffer (); // Find codetype. String codetype; if (attrMap == null) { codetype = null; } else if (attrMap.getNamedItem ("codetype") == null) { codetype = null; } else { codetype = attrMap.getNamedItem ("codetype").getNodeValue (); } // Check tag requirements. if ((attrMap == null) || (codetype == null) || (!codetype.equals ("application/xid")) || (attrMap.getNamedItem ("data") == null)) { // STU: do default action. Presenter.processElementBasically (node, datas, webappPath, errorOutput); } else { log.debug ("object action"); result.append (""); // Build the file name. String htmlFileName = webappPath + attrMap.getNamedItem ("data").getNodeValue (); // Load file in tree. Document childDoc = null; try { childDoc = fileToTree (htmlFileName, errorOutput); } catch (Exception ex) { result.append ("unable to build the file tree"); log.debug ("unable to build the file tree"); } // Extract the 'body' section. Node body = null; try { NodeList nodes = childDoc.getElementsByTagName ("body"); if (nodes.getLength () == 0) { result.append ("no body tag in include html"); log.debug ("no body tag in include html"); } else { body = nodes.item(0); } } catch (Exception e) { result.append ("error getting child"); log.debug ("error getting child"); } // Process the body child as part of the primary tree. NodeList bodyChildren = body.getChildNodes (); if (bodyChildren != null) { int childCount = bodyChildren.getLength (); for (int childCounter = 0; childCounter < childCount; childCounter++) { result.append (process (bodyChildren.item (childCounter), datas, webappPath, errorOutput)); } } // result.append (""); } log.debug ("end of object action"); // return (result); } /** * Processes a node that has dynamic content. Calls the appropriate code * generator method, depending on the tag. * * @param node * Current node. * @param attrs * The tag attributes. * @param idAttr * The ID. */ static protected StringBuffer processElementWithId (Node node, NamedNodeMap attrs, Node idAttr, TagsDataById datas, String webappPath, StringBuffer errorOutput) { StringBuffer result; result = new StringBuffer (); String tag = node.getNodeName(); String idValue = idAttr.getNodeValue(); log.debug ("tag=" + tag); // Get data of this id. TagDataCore dataCore = datas.get (idAttr.getNodeValue ()); if (dataCore == null) { result.append (Presenter.processElementBasically (node, datas, webappPath, errorOutput)); } else if (dataCore instanceof TagData) { TagData data = (TagData) dataCore; String theClass; if (data == null) { theClass = null; } else { theClass = data.getAttributes ().getAttribute ("class"); } if ((theClass == null) || (!theClass.equals ("xid:nodisplay"))) { // Open the tag. result.append ("<"); result.append (node.getNodeName()); // Build attributes. result.append (processAttributes (attrs, data)); if ((node.getChildNodes () == null) && ((data == null) || data.display ().equals (""))) { // Close the tag. result.append (" />"); } else { result.append ('>'); // Insert data. if ((data == null) || (data.display ().equals (""))) { result.append (processChildren (node, datas, webappPath, errorOutput)); } else { result.append (data.display ()); } // Close the tag. result.append ("'); } } } else if (dataCore instanceof TagsData) { TagsData data = (TagsData) dataCore; } else if (dataCore instanceof TagsDataById) { TagsDataById data = (TagsDataById) dataCore; } else { log.warn ("Unknow type of TagDataId"); } // log.debug ("Exit"); return (result); } /** * Recursive method that processes a node and any child nodes. * */ static protected StringBuffer process (Node node, TagsDataById datas, String webappPath, StringBuffer errorOutput) { log.debug ("Enter"); String TRANSITIONAL_DTD = "xhtml1-transitional.dtd"; String TRANSITIONAL_DOCTYPE = "\n"; StringBuffer result; result = new StringBuffer (); // Is there anything to do? if (node != null) { log.debug ("nodeName=" + node.getNodeName ()); // Find the name attribute value. String name; name = getClassAttributeValue (node); if ((name == null) || ((name != null) && (!name.equals ("xid:nodisplay")))) { int type = node.getNodeType(); switch (type) { case Node.DOCUMENT_NODE: { log.debug ("case Node.DOCUMENT_NODE"); DocumentType dt = ((Document) node).getDoctype(); if (dt != null) { String publicId = dt.getPublicId(); String systemId = dt.getSystemId(); if (systemId.equals(TRANSITIONAL_DTD)) { result.append(TRANSITIONAL_DOCTYPE); } // Log.write(Log.TRACE,"publicId = " + publicId); // Log.write(Log.TRACE,"systemId = " + systemId); } result.append (Presenter.process (((Document) node).getDocumentElement(), datas, webappPath, errorOutput)); break; } case Node.ELEMENT_NODE: { log.debug ("case Node.ELEMENT_NODE"); NamedNodeMap attrs = node.getAttributes (); Node idAttr = attrs.getNamedItem ("id"); if (idAttr != null) { result.append (Presenter.processElementWithId (node, attrs, idAttr, datas, webappPath, errorOutput)); } else { result.append (Presenter.processElementBasically (node, datas, webappPath, errorOutput)); } break; } // handle entity reference nodes case Node.ENTITY_REFERENCE_NODE: { log.debug ("case Node.ENTITY_REFERENCE_NODE"); result.append ('&'); result.append (node.getNodeName()); result.append (';'); break; } // print cdata sections case Node.CDATA_SECTION_NODE: { log.debug ("case Node.CDATA_SECTION_NODE"); result.append (""); break; } // print text case Node.TEXT_NODE: { log.debug ("case Node.TEXTE_NODE"); result.append (restoreEntities (new StringBuffer(node.getNodeValue()))); break; } // print processing instruction case Node.PROCESSING_INSTRUCTION_NODE: { log.debug ("Node.PROCESSING_INSTRUCTION_NODE"); result.append (" 0)) { result.append (' '); result.append (data); } result.append ("?>"); break; } } } } // //log.info ("result=" + result); log.debug ("Exit"); return (result); } /* */ static StringBuffer processElementBasically (Node node, TagsDataById datas, String webappPath, StringBuffer errorOutput) { StringBuffer result; result = new StringBuffer (); // Open the tag. result.append ('<'); result.append (node.getNodeName()); // Build the tag attributes. NamedNodeMap attrs = node.getAttributes (); if (attrs != null) { for (int i = 0; i < attrs.getLength(); i++) { Attr attr = (Attr) attrs.item(i); result.append (' '); result.append (attr.getNodeName()); result.append ("=\""); result.append (restoreEntities(new StringBuffer(attr.getNodeValue()))); result.append ("\""); } // NodeList children = node.getChildNodes(); if (children == null) { result.append(" />"); } else { int childrenCount = children.getLength (); if (childrenCount == 0) { result.append(" />"); } else { result.append('>'); for (int i = 0; i < childrenCount; i++) { result.append (process (children.item(i), datas, webappPath, errorOutput)); } result.append("'); } } } // return (result); } /* * */ static protected Document buildTree (InputStream source, StringBuffer errorOutput) { Document result; try { // Create a DocumentBuilderFactory and configure it. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance (); // Set various configuration options. dbf.setValidating (true); dbf.setIgnoringComments (true); dbf.setIgnoringElementContentWhitespace (false); dbf.setCoalescing (false); // Keep entity references as they are. dbf.setExpandEntityReferences(false); // Create a DocumentBuilder that satisfies the constraints // specified by the DocumentBuilderFactory. DocumentBuilder db = dbf.newDocumentBuilder (); ParserErrorHandler errorHandler; errorHandler = new ParserErrorHandler(); // Set the error handler. db.setErrorHandler (errorHandler); Schema schema = db.getSchema (); log.debug ("schema=" + schema); // Parse the input file. result = db.parse (source); if (errorHandler.hasError ()) { errorOutput.append (errorHandler.toString ()); } } catch (ParserConfigurationException exception) { String errorMessage = "Parser configuration exception: " + exception.getMessage (); errorOutput.append (errorMessage); log.error (errorMessage); result = null; } catch (SAXException exception) { String errorMessage = "Error during SAX parsing: " + exception.getMessage (); errorOutput.append (errorMessage); log.error (errorMessage); result = null; } catch (IOException exception) { String errorMessage = "IOError during parsing." + exception.getMessage (); errorOutput.append (errorMessage); log.error (errorMessage); result = null; } // return (result); } /* * */ static protected Document fileToTree (String fileName, StringBuffer errorOutput) { Document result; try { result = buildTree (new FileInputStream (new File (fileName)), errorOutput); } catch (IOException exception) { String errorMessage = "IOError during parsing." + exception.getMessage (); errorOutput.append (errorMessage); log.error (errorMessage); result = null; } // return (result); } /* * */ static protected void addMetaTag (Document doc, String name, String content) { // Find head tag. Node headNode = Presenter.findHeadNode (doc); Node metaNode = doc.createElement ("meta"); NamedNodeMap attrMap = metaNode.getAttributes(); Node attrNode = doc.createAttribute("name"); attrMap.setNamedItem(attrNode); attrNode.setNodeValue(name); attrNode = doc.createAttribute("content"); attrMap.setNamedItem(attrNode); attrNode.setNodeValue(content); headNode.insertBefore(metaNode, headNode.getFirstChild()); } /** * Finds the node containing the <head> tag. * * @param node * Document node. * @return The head tag node */ static protected Node findHeadNode (Node node) { Node headNode = null; int type = node.getNodeType(); switch (type) { // print document case Node.DOCUMENT_NODE: { headNode = findHeadNode(((Document) node).getDocumentElement()); break; } case Node.ELEMENT_NODE: { String tag = node.getNodeName(); if ("head".equals(tag)) { headNode = node; break; } NodeList children = node.getChildNodes(); int numChildren = 0; if (children != null) numChildren = children.getLength(); for (int i = 0; i < numChildren; i++) { headNode = findHeadNode(children.item(i)); if (headNode != null) break; } break; } } return headNode; } /** * Any ampersand lt;, ampersand gt; and ampersand amp; sequences in text * nodes get read in as symbols. This method converts them back to entities. * * @param s String that is to have the entities restored.. * @return The processed string. */ static public String restoreEntities (StringBuffer s) { String result; if (s == null) { result = null; } else { StringBuffer str = new StringBuffer(); int len = (s != null) ? s.length() : 0; for (int i = 0; i < len; i++) { char ch = s.charAt(i); switch (ch) { case '<': { str.append("<"); break; } case '>': { str.append(">"); break; } case '&': { str.append("&"); break; } default: { str.append(ch); } } } result = str.toString (); } // return (result); } /** * Get the text for an element. Converts new lines to spaces. * * @param node */ static protected String getElementText (Node node) { String result; result = ""; // Grrrr, Java ... NodeList children = node.getChildNodes(); if (children == null) { result = ""; } else { boolean ended = false; int childCounter = 0; int childCount = children.getLength (); while (!ended) { if (childCounter >= childCount) { ended = true; result = ""; } else { Node child = children.item (childCounter); if (child.getNodeType () == Node.TEXT_NODE) { result = newLinesToSpaces (child.getNodeValue ()); // STU (+=, newLines...) ended = true; } else { childCounter += 1; } } } } // return (result); } /** * Converts New Line characters to spaces. This is used when for example * the text in a div tag goes over serveral lines. * * @param text String * @return String */ static protected String newLinesToSpaces (String text) { StringBuffer result = new StringBuffer (text); for (int i = 0; i < result.length(); i++) { if (result.charAt (i) == '\n') { result.setCharAt (i,' '); } } // return (result.toString()); } /* * */ static protected StringBuffer processAttributes (NamedNodeMap attrs, TagData model) { StringBuffer result; result = new StringBuffer (); // Build the original attributes list. HashMap mergedAttributes; mergedAttributes = new HashMap (); for (int attributeCounter = 0; attributeCounter < attrs.getLength(); attributeCounter++) { Attr attr = (Attr) attrs.item (attributeCounter); mergedAttributes.put (attr.getNodeName(), attr.getNodeValue ()); } // Put model attributes in the merged attributes list. if (model != null) { Attributes modelAttributes = model.getAttributes(); if (modelAttributes != null) { Iterator iterator = modelAttributes.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry attribute = (Map.Entry) iterator.next(); if (mergedAttributes.containsKey (attribute.getKey ())) { if (attribute.getKey ().equalsIgnoreCase ("style")) { mergedAttributes.put (attribute.getKey (), mergedAttributes.get (attribute.getKey ()) + attribute.getValue ()); } else { mergedAttributes.put (attribute.getKey (), attribute.getValue ()); } } else { mergedAttributes.put (attribute.getKey (), attribute.getValue ()); } } } } // Display the attributes Iterator iterator = mergedAttributes.entrySet().iterator(); while (iterator.hasNext ()) { Map.Entry attribute = (Map.Entry) iterator.next(); result.append(" " + attribute.getKey () + "=\"" + attribute.getValue ()+ "\""); } // return (result); } /* * */ static public String extractBodyContent (StringBuffer data) { String result = null; // Extract the body content. String dataLowerCase = data.toString ().toLowerCase (); int startBody = dataLowerCase.indexOf (""); int endBody = dataLowerCase.indexOf (""); // Note: as failed search is improbable, no care about complexity // in failed search case. if ((startBody == -1) || (endBody == -1)) { result = null; } else { result = data.substring (startBody + 6, endBody).trim (); } // return (result); } }