Ignore:
Timestamp:
2016-04-20T22:50:52+12:00 (8 years ago)
Author:
davidb
Message:

Changes in the Java code to support the new approach taken to client-side XSLT (using Saxon-CE JS library in the browser -- see next commit). Also some better error reporting when processing XSLT files

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/core/TransformingReceptionist.java

    r29997 r30477  
    88import java.util.HashMap;
    99import java.util.HashSet;
     10import java.util.regex.Pattern;
     11import java.util.regex.Matcher;
    1012
    1113import javax.xml.transform.Transformer;
     
    2729import org.greenstone.gsdl3.util.XMLConverter;
    2830import org.greenstone.gsdl3.util.XMLTransformer;
     31import org.greenstone.gsdl3.util.XSLTUtil;
    2932import org.greenstone.util.GlobalProperties;
    3033import org.w3c.dom.Comment;
     
    200203        for (File currentFile : xslFiles)
    201204        {
     205
     206            String full_filename = currentFile.getPath();
     207            int sep_pos = full_filename.lastIndexOf(File.separator)+1;
     208            String local_filename = full_filename.substring(sep_pos);
     209            if (local_filename.startsWith(".")) {
     210            logger.warn("Greenstone does not normally rely on 'dot' files for XSL transformations.\n Is the following file intended to be part of the digital library installation?\n XSL File being read in:\n    " + currentFile.getPath());
     211            }
     212               
    202213            Document currentDoc = this.converter.getDOM(currentFile);
    203214            if (currentDoc == null)
     
    401412                }
    402413            }
    403         }
     414    }
    404415        request.appendChild(request.getOwnerDocument().importNode(extraMetadataList, true));
    405416    }
     
    409420        // might need to add some data to the page
    410421        addExtraInfo(page);
     422
     423       
    411424        // transform the page using xslt
    412         Node transformed_page = transformPage(page);
    413 
     425
     426        String currentInterface = (String) config_params.get(GSConstants.INTERFACE_NAME);
     427
     428        Element request = (Element) GSXML.getChildByTagName(page, GSXML.PAGE_REQUEST_ELEM);
     429        String output = request.getAttribute(GSXML.OUTPUT_ATT);
     430
     431        boolean useClientXSLT = (Boolean) config_params.get(GSConstants.USE_CLIENT_SIDE_XSLT);
     432        //logger.info("Client side transforms allowed? " + allowsClientXSLT);
     433
     434        if (useClientXSLT)
     435        {
     436            // if not specified, output defaults to 'html', but this isn't what we want when useClientXSLT is on
     437            if (output.equals("html")) {
     438            output = "xsltclient";
     439            }
     440        }
     441        Node transformed_page = transformPage(page,currentInterface,output);
     442
     443        if (useClientXSLT) {
     444            return transformed_page;
     445        }
    414446        // if the user has specified they want only a part of the full page then subdivide it
    415447        boolean subdivide = false;
    416448        String excerptID = null;
    417449        String excerptTag = null;
    418         Element request = (Element) GSXML.getChildByTagName(page, GSXML.PAGE_REQUEST_ELEM);
    419450        Element cgi_param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
    420451        if (cgi_param_list != null)
     
    517548    }
    518549
     550        protected void replaceNodeWithInterfaceText(Document doc, String interface_name, String lang,
     551                            Element elem, String attr_name, String attr_val)
     552        {
     553        String pattern_str_3arg = "util:getInterfaceText\\([^,]+,[^,]+,\\s*'(.+?)'\\s*\\)";
     554        String pattern_str_4arg = "util:getInterfaceText\\([^,]+,[^,]+,\\s*'(.+?)'\\s*,\\s*(.+?)\\s*\\)$";
     555   
     556        Pattern pattern3 = Pattern.compile(pattern_str_3arg);
     557        Matcher matcher3 = pattern3.matcher(attr_val);
     558        if (matcher3.find()) {
     559        String dict_key = matcher3.group(1);
     560        String dict_val = XSLTUtil.getInterfaceText(interface_name,lang,dict_key);
     561       
     562        Node parent_node = elem.getParentNode();
     563
     564        Text replacement_text_node = doc.createTextNode(dict_val);
     565            parent_node.replaceChild(replacement_text_node,elem);
     566        }       
     567        else {
     568        Pattern pattern4 = Pattern.compile(pattern_str_4arg);
     569        Matcher matcher4 = pattern4.matcher(attr_val);
     570        StringBuffer string_buffer4 = new StringBuffer();
     571
     572        if (matcher4.find()) {
     573            String dict_key = matcher4.group(1);
     574            String args     = matcher4.group(2);
     575            args = args.replaceAll("\\$","\\\\\\$");
     576           
     577            String dict_val = XSLTUtil.getInterfaceText(interface_name,lang,dict_key);
     578
     579            matcher4.appendReplacement(string_buffer4, "js:getInterfaceTextSubstituteArgs('"+dict_val+"',string("+args+"))");
     580            matcher4.appendTail(string_buffer4);
     581
     582            attr_val = string_buffer4.toString();
     583            elem.setAttribute(attr_name,attr_val);
     584        }
     585        else {
     586            logger.error("Failed to find match in attribute: " + attr_name + "=\"" + attr_val + "\"");
     587            attr_val = attr_val.replaceAll("util:getInterfaceText\\(.+?,.+?,\\s*(.+?)\\s*\\)","$1");
     588            elem.setAttribute(attr_name,attr_val);
     589        }
     590        }
     591   
     592    }
     593   
     594        protected void resolveExtendedNamespaceAttributesXSLT(Document doc, String interface_name, String lang)
     595        {
     596        String[] attr_list = new String[] {"select","test"};
     597
     598        // http://stackoverflow.com/questions/13220520/javascript-replace-child-loop-issue
     599        // go through nodeList in reverse to avoid the 'skipping' problem, due to
     600        // replaceChild() calls removing items from the "live" nodeList
     601       
     602        NodeList nodeList = doc.getElementsByTagName("*");
     603        for (int i=nodeList.getLength()-1; i>=0; i--) {
     604        Node node = nodeList.item(i);
     605        if (node.getNodeType() == Node.ELEMENT_NODE) {
     606            Element elem = (Element)node;       
     607            for (String attr_name : attr_list) {
     608            if (elem.hasAttribute(attr_name)) {
     609                String attr_val = elem.getAttribute(attr_name);
     610               
     611                if (attr_val.startsWith("util:getInterfaceText(")) {
     612                // replace the node with dictionary lookup
     613                replaceNodeWithInterfaceText(doc, interface_name,lang, elem,attr_name,attr_val);
     614                }                           
     615                else if (attr_val.contains("util:")) {
     616
     617                attr_val = attr_val.replaceAll("util:getInterfaceStringsAsJavascript\\(.+?,.+?,\\s*(.+?)\\)","$1");
     618
     619                //attr_val = attr_val.replaceAll("util:escapeNewLinesAndQuotes\\(\\s*(.+?)\\s*\\)","'escapeNLandQ $1'");
     620                //attr_val = attr_val.replaceAll("util:escapeNewLinesAndQuotes\\(\\s*(.+?)\\s*\\)","$1");                   
     621
     622                // 'contains()' supported in XSLT 1.0, so OK to change any util:contains() into contains()
     623                attr_val = attr_val.replaceAll("util:(contains\\(.+?\\))","$1");
     624
     625                elem.setAttribute(attr_name,attr_val);
     626                }
     627
     628                if (attr_val.contains("java:")) {
     629                if (attr_val.indexOf("getInterfaceTextSubstituteArgs")>=4) {
     630                   
     631                    attr_val = attr_val.replaceAll("java:.+?\\.(\\w+)\\((.*?)\\)$","js:$1($2)");
     632                }
     633               
     634                elem.setAttribute(attr_name,attr_val);
     635                }
     636            }
     637            }
     638           
     639        }
     640        }
     641    }
     642
     643
     644        protected void resolveExtendedNamespaceAttributesXML(Document doc, String interface_name, String lang)
     645        {
     646        String[] attr_list = new String[] {"src", "href"};
     647
     648        // http://stackoverflow.com/questions/13220520/javascript-replace-child-loop-issue
     649        // go through nodeList in reverse to avoid the 'skipping' problem, due to
     650        // replaceChild() calls removing items from the "live" nodeList
     651       
     652        NodeList nodeList = doc.getElementsByTagName("*");
     653        for (int i=nodeList.getLength()-1; i>=0; i--) {
     654        Node node = nodeList.item(i);
     655        if (node.getNodeType() == Node.ELEMENT_NODE) {
     656            Element elem = (Element)node;
     657            for (String attr_name : attr_list) {
     658            if (elem.hasAttribute(attr_name)) {
     659                String attr_val = elem.getAttribute(attr_name);
     660
     661                if (attr_val.contains("util:getInterfaceText(")) {
     662                String pattern_str_3arg = "util:getInterfaceText\\([^,]+,[^,]+,\\s*'(.+?)'\\s*\\)";
     663                Pattern pattern3 = Pattern.compile(pattern_str_3arg);
     664                Matcher matcher3 = pattern3.matcher(attr_val);
     665               
     666                StringBuffer string_buffer3 = new StringBuffer();
     667               
     668                boolean found_match = false;
     669               
     670                while (matcher3.find()) {
     671                    found_match = true;
     672                    String dict_key = matcher3.group(1);
     673                    String dict_val = XSLTUtil.getInterfaceText(interface_name,lang,dict_key);
     674                   
     675                    matcher3.appendReplacement(string_buffer3, dict_val);
     676                }
     677                matcher3.appendTail(string_buffer3);
     678               
     679                if (found_match) {
     680                    attr_val = string_buffer3.toString();
     681                    elem.setAttribute(attr_name,attr_val);                 
     682                }
     683                else {             
     684                    logger.error("Failed to find match in attribute: " + attr_name + "=\"" + attr_val + "\"");
     685                    attr_val = attr_val.replaceAll("util:getInterfaceText\\(.+?,.+?,\\s*(.+?)\\s*\\)","$1");
     686                    elem.setAttribute(attr_name,attr_val);
     687                }
     688                }
     689                else if (attr_val.contains("util:")) {
     690
     691                logger.error("Encountered unexpected 'util:' prefix exension: " + attr_name + "=\"" + attr_val + "\"");
     692                }
     693
     694                if (attr_val.contains("java:")) {
     695                // make anything java: safe from the point of an XSLT without extensions
     696                logger.error("Encountered unexpected 'java:' prefix exension: " + attr_name + "=\"" + attr_val + "\"");
     697
     698                }
     699            }
     700            }
     701           
     702        }
     703        }
     704    }
     705
     706   
     707   
    519708    /**
    520709     * overwrite this to add any extra info that might be needed in the page
     
    529718     * the page and add it to the xslt before transforming
    530719     */
    531     protected Node transformPage(Element page)
     720        protected Node transformPage(Element page,String currentInterface,String output)
    532721    {
    533722        _debug = false;
    534723
    535         boolean allowsClientXSLT = (Boolean) config_params.get(GSConstants.ALLOW_CLIENT_SIDE_XSLT);
    536         //System.out.println("Client side transforms allowed? " + allowsClientXSLT);
    537 
    538         String currentInterface = (String) config_params.get(GSConstants.INTERFACE_NAME);
    539 
    540724        Element request = (Element) GSXML.getChildByTagName(page, GSXML.PAGE_REQUEST_ELEM);
    541         String output = request.getAttribute(GSXML.OUTPUT_ATT);
    542 
    543         //System.out.println("Current output mode is: " + output + ", current interface name is: " + currentInterface);
    544 
    545         if (allowsClientXSLT)
    546         {
    547             if (!currentInterface.endsWith(GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX) && output.equals("html"))
    548             {
    549                 System.out.println("output is html and we are not currently using a client side version, switching");
    550                 // Switch the interface
    551                 config_params.put(GSConstants.INTERFACE_NAME, currentInterface.concat(GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX));
    552             }
    553             else if ((currentInterface.endsWith(GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX) && !output.equals("html")) || output.equals("server"))
    554             {
    555                 // The reverse needs to happen too
    556                 config_params.put(GSConstants.INTERFACE_NAME, currentInterface.substring(0, currentInterface.length() - GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX.length()));
    557             }
    558         }
    559         else if (currentInterface.endsWith(GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX))
    560         {
    561             config_params.put(GSConstants.INTERFACE_NAME, currentInterface.substring(0, currentInterface.length() - GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX.length()));
    562         }
    563 
     725
     726        //logger.info("Current output mode is: " + output + ", current interface name is: " + currentInterface);
     727       
    564728        // DocType defaults in case the skin doesn't have an "xsl:output" element
    565729        String qualifiedName = "html";
     
    573737        if (output.equals("xsltclient"))
    574738        {
     739                String baseURL = request.getAttribute(GSXML.BASE_URL);
     740               
    575741            // If you're just getting the client-side transform page, why bother with the rest of this?
    576742            Element html = docWithDoctype.createElement("html");
    577743            Element img = docWithDoctype.createElement("img");
    578             img.setAttribute("src", "interfaces/default/images/loading.gif"); // Make it dynamic
     744            img.setAttribute("src", "loading.gif"); // Make it dynamic
    579745            img.setAttribute("alt", "Please wait...");
    580746            Text title_text = docWithDoctype.createTextNode("Please wait..."); // Make this language dependent
    581747            Element head = docWithDoctype.createElement("head");
     748
     749            // e.g., <base href="http://localhost:8383/greenstone3/" /><!-- [if lte IE 6]></base><![endif] -->
     750            Element base = docWithDoctype.createElement("base");
     751            base.setAttribute("href",baseURL);
     752            Comment opt_end_base = docWithDoctype.createComment("[if lte IE 6]></base><![endif]");
     753           
    582754            Element title = docWithDoctype.createElement("title");
    583755            title.appendChild(title_text);
     756
    584757            Element body = docWithDoctype.createElement("body");
     758
     759            Element jquery_script = docWithDoctype.createElement("script");
     760            jquery_script.setAttribute("src", "jquery-1.10-min.js");
     761            jquery_script.setAttribute("type", "text/javascript");
     762            Comment jquery_comment = docWithDoctype.createComment("jQuery");
     763            jquery_script.appendChild(jquery_comment);
     764
     765            Element saxonce_script = docWithDoctype.createElement("script");
     766            saxonce_script.setAttribute("src", "Saxonce/Saxonce.nocache.js");
     767            saxonce_script.setAttribute("type", "text/javascript");
     768            Comment saxonce_comment = docWithDoctype.createComment("SaxonCE");
     769            saxonce_script.appendChild(saxonce_comment);
     770
     771            Element xsltutil_script = docWithDoctype.createElement("script");
     772            xsltutil_script.setAttribute("src", "xslt-util.js");
     773            xsltutil_script.setAttribute("type", "text/javascript");
     774            Comment xsltutil_comment = docWithDoctype.createComment("JavaScript version of XSLTUtil.java");
     775            xsltutil_script.appendChild(xsltutil_comment);
     776
    585777            Element script = docWithDoctype.createElement("script");
    586             Element jquery = docWithDoctype.createElement("script");
    587             jquery.setAttribute("src", "jquery.js");
    588             jquery.setAttribute("type", "text/javascript");
    589             Comment jquery_comment = docWithDoctype.createComment("jQuery");
    590778            Comment script_comment = docWithDoctype.createComment("Filler for browser");
    591             script.setAttribute("src", "test.js");
     779            script.setAttribute("src", "client-side-xslt.js");
    592780            script.setAttribute("type", "text/javascript");
     781            script.appendChild(script_comment);
     782           
    593783            Element pagevar = docWithDoctype.createElement("script");
    594784            Element style = docWithDoctype.createElement("style");
     
    599789
    600790            html.appendChild(head);
     791            head.appendChild(base); head.appendChild(opt_end_base);
    601792            head.appendChild(title);
    602793            head.appendChild(style);
     
    604795            html.appendChild(body);
    605796            head.appendChild(pagevar);
    606             head.appendChild(jquery);
     797            head.appendChild(jquery_script);
     798            head.appendChild(saxonce_script);
     799            head.appendChild(xsltutil_script);
    607800            head.appendChild(script);
    608801            pagevar.appendChild(page_var_text);
    609             jquery.appendChild(jquery_comment);
    610             script.appendChild(script_comment);
     802
    611803            body.appendChild(img);
    612804            docWithDoctype.appendChild(html);
     
    653845            siteName.appendChild(siteNameText);
    654846
     847            Element clientSideXSLTName = theXML.createElement("param");
     848            clientSideXSLTName.setAttribute("name", "use_client_side_xslt");
     849            Boolean useClientXSLT = (Boolean) config_params.get(GSConstants.USE_CLIENT_SIDE_XSLT);
     850            Text clientSideXSLTNameText = theXML.createTextNode(useClientXSLT.toString());
     851            clientSideXSLTName.appendChild(clientSideXSLTNameText);
     852           
    655853            Element filepath = theXML.createElement("param");
    656854            filepath.setAttribute("name", "filepath");
     
    661859            root.appendChild(intname);
    662860            root.appendChild(siteName);
     861            root.appendChild(clientSideXSLTName);
    663862            root.appendChild(filepath);
    664863
     
    9161115            return converter.getDOM(getStringFromDocument(skinAndLibraryXsl));
    9171116        }
    918         if (output.equals("skinandlibdoc") || output.equals("clientside"))
     1117        if (output.equals("skinandlibdoc"))
    9191118        {
    9201119
    9211120            Node skinAndLib = converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
    922 
    923             if (output.equals("skinandlibdoc"))
    924             {
    925                 return skinAndLib;
    926             }
    927             else
    928             {
    929                 // Send XML and skinandlibdoc down the line together
    930                 Document finalDoc = converter.newDOM();
    931                 Node finalDocSkin = finalDoc.importNode(skinAndLibraryDoc.getDocumentElement(), true);
    932                 Node finalDocXML = finalDoc.importNode(theXML.getDocumentElement(), true);
    933                 Element root = finalDoc.createElement("skinlibPlusXML");
    934                 root.appendChild(finalDocSkin);
    935                 root.appendChild(finalDocXML);
    936                 finalDoc.appendChild(root);
    937                 return (Node) finalDoc.getDocumentElement();
    938             }
     1121            return skinAndLib;         
    9391122        }
    9401123        if (output.equals("oldskindoc"))
     
    10181201        }
    10191202
    1020         if (output.equals("skinandlibdocfinal"))
    1021         {
    1022             return converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
     1203        if (output.equals("skinandlibdocfinal") || output.equals("clientside"))
     1204        {
     1205            if (output.equals("skinandlibdocfinal"))
     1206            {
     1207                    Node skinAndLibFinal = converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
     1208                return skinAndLibFinal;
     1209            }
     1210            else
     1211            {
     1212                    // Go through and 'fix up' any 'util:...' or 'java:...' attributes the skinAndLibraryDoc has
     1213                    String lang = (String)config_params.get("lang");
     1214                    resolveExtendedNamespaceAttributesXSLT(skinAndLibraryDoc,currentInterface,lang); // test= and select= attributes
     1215                resolveExtendedNamespaceAttributesXML(skinAndLibraryDoc,currentInterface,lang);  // href= and src= attributes
     1216                Node skinAndLibFinal = converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
     1217               
     1218                // Send XML and skinandlibdoc down the line together
     1219                Document finalDoc = converter.newDOM();
     1220                Node finalDocSkin = finalDoc.importNode(skinAndLibraryDoc.getDocumentElement(), true);
     1221                Node finalDocXML = finalDoc.importNode(theXML.getDocumentElement(), true);
     1222                Element root = finalDoc.createElement("skinlibfinalPlusXML");
     1223                root.appendChild(finalDocSkin);
     1224                root.appendChild(finalDocXML);
     1225                finalDoc.appendChild(root);
     1226                return (Node) finalDoc.getDocumentElement();
     1227            }
     1228
     1229
     1230
     1231           
    10231232        }
    10241233
Note: See TracChangeset for help on using the changeset viewer.