Initialize
[sdk/ide/product.git] / org.eclipse.jst.pagedesigner / src / org / eclipse / jst / pagedesigner / dtmanager / converter / internal / DTTagConverterDecorator.java
1 /*******************************************************************************\r
2  * Copyright (c) 2005 Oracle Corporation.\r
3  * All rights reserved. This program and the accompanying materials\r
4  * are made available under the terms of the Eclipse Public License v1.0\r
5  * which accompanies this distribution, and is available at\r
6  * http://www.eclipse.org/legal/epl-v10.html\r
7  *\r
8  * Contributors:\r
9  *    Oracle Corporation - initial API and implementation\r
10  *******************************************************************************/\r
11 package org.eclipse.jst.pagedesigner.dtmanager.converter.internal;\r
12 \r
13 import java.net.URL;\r
14 import java.util.HashMap;\r
15 import java.util.Map;\r
16 \r
17 import javax.servlet.jsp.el.ELException;\r
18 import javax.xml.xpath.XPath;\r
19 import javax.xml.xpath.XPathConstants;\r
20 import javax.xml.xpath.XPathExpressionException;\r
21 import javax.xml.xpath.XPathFactory;\r
22 \r
23 import org.eclipse.core.runtime.ISafeRunnable;\r
24 import org.eclipse.core.runtime.SafeRunner;\r
25 import org.eclipse.jface.resource.ImageDescriptor;\r
26 import org.eclipse.jst.jsf.common.metadata.Trait;\r
27 import org.eclipse.jst.jsf.common.metadata.internal.IImageDescriptorProvider;\r
28 import org.eclipse.jst.jsf.common.metadata.internal.IMetaDataSourceModelProvider;\r
29 import org.eclipse.jst.jsf.common.metadata.internal.IResourceURLProvider;\r
30 import org.eclipse.jst.pagedesigner.PDPlugin;\r
31 import org.eclipse.jst.pagedesigner.converter.IConverterFactory;\r
32 import org.eclipse.jst.pagedesigner.converter.ITagConverter;\r
33 import org.eclipse.jst.pagedesigner.dtmanager.DTManager;\r
34 import org.eclipse.jst.pagedesigner.dtmanager.IDTInfo;\r
35 import org.eclipse.jst.pagedesigner.dtmanager.converter.ITagConverterDecorator;\r
36 import org.eclipse.jst.pagedesigner.dtmanager.dtinfo.ResolveAttributeValue;\r
37 import org.eclipse.jst.pagedesigner.dtmanager.dtinfo.TagDecorateInfo;\r
38 import org.eclipse.jst.pagedesigner.preview.PageExpressionContext;\r
39 import org.w3c.dom.Element;\r
40 import org.w3c.dom.Node;\r
41 import org.w3c.dom.NodeList;\r
42 import org.w3c.dom.Text;\r
43 \r
44 /**\r
45  * ITagConverterDecorator implementation for DTTagConverter.\r
46  * \r
47  * @author Ian Trimble - Oracle\r
48  */\r
49 public class DTTagConverterDecorator implements ITagConverterDecorator\r
50 {\r
51 \r
52     private static final String DECORATE_INFO_ID_DESIGN = "vpd-decorate-design"; //$NON-NLS-1$\r
53     private static final String DECORATE_INFO_ID_PREVIEW = "vpd-decorate-preview"; //$NON-NLS-1$\r
54     private static final String MD_PLUGIN_LOCATION = "$metadata-plugin-location$"; //$NON-NLS-1$\r
55 \r
56     /*\r
57      * (non-Javadoc)\r
58      * \r
59      * @see\r
60      * org.eclipse.jst.pagedesigner.dtmanager.converter.ITagConverterDecorator\r
61      * #decorate(org.eclipse.jst.pagedesigner.converter.ITagConverter)\r
62      */\r
63     public void decorate(ITagConverter tagConverter)\r
64     {\r
65         if (!(tagConverter instanceof DTTagConverter))\r
66         {\r
67             throw new IllegalArgumentException(\r
68                     "ITagConverter argument must be an instance of DTTagConverter"); //$NON-NLS-1$\r
69         }\r
70         DTTagConverter dtTagConverter = (DTTagConverter) tagConverter;\r
71 \r
72         if (dtTagConverter.getMode() == IConverterFactory.MODE_DESIGNER)\r
73         {\r
74             decorateFromDTInfo(dtTagConverter, DECORATE_INFO_ID_DESIGN);\r
75         } else if (dtTagConverter.getMode() == IConverterFactory.MODE_PREVIEW)\r
76         {\r
77             decorateFromDTInfo(dtTagConverter, DECORATE_INFO_ID_PREVIEW);\r
78         }\r
79 \r
80         if (tagConverter.getResultElement() == null\r
81                 && tagConverter.isVisualByHTML())\r
82         {\r
83             createUnknownTagRepresentation(dtTagConverter);\r
84         }\r
85     }\r
86 \r
87     /**\r
88      * Performs decoration of the specified DTTagConverter instance from IDTInfo\r
89      * (metadata) for the specified (by ID) TagDecorateInfo.\r
90      * \r
91      * @param dtTagConverter\r
92      *            DTTagConverter instance.\r
93      * @param tagDecorateInfoID\r
94      *            ID of the TagDecorateInfo to be located in metadata.\r
95      */\r
96     protected void decorateFromDTInfo(DTTagConverter dtTagConverter,\r
97             String tagDecorateInfoID)\r
98     {\r
99         Element srcElement = dtTagConverter.getHostElement();\r
100         DTManager dtManager = DTManager.getInstance();\r
101         IDTInfo dtInfo = dtManager.getDTInfo(srcElement);\r
102         if (dtInfo != null)\r
103         {\r
104             TagDecorateInfo tdInfo = dtInfo\r
105                     .getTagDecorateInfo(tagDecorateInfoID);\r
106             if (tdInfo != null)\r
107             {\r
108                 dtTagConverter.setMultiLevel(tdInfo.isMultiLevel());\r
109                 dtTagConverter.setNeedBorderDecorator(tdInfo\r
110                         .isNeedBorderDecorator());\r
111                 dtTagConverter.setNeedTableDecorator(tdInfo\r
112                         .isNeedTableDecorator());\r
113                 if (tdInfo.isNonVisual())\r
114                 {\r
115                     setNonVisual(dtTagConverter, dtInfo, tdInfo\r
116                             .getNonVisualImagePath());\r
117                 }\r
118                 if (tdInfo.isResolveChildText())\r
119                 {\r
120                     resolveChildText(dtTagConverter.getResultElement(), dtInfo);\r
121                 }\r
122                 if (tdInfo.isSetNonVisualChildElements())\r
123                 {\r
124                     setNonVisualChildElements(dtTagConverter, srcElement);\r
125                 }\r
126                 dtTagConverter.setWidget(tdInfo.isWidget());\r
127                 dtTagConverter.setMinHeight(tdInfo.getMinHeight());\r
128                 dtTagConverter.setMinWidth(tdInfo.getMinWidth());\r
129                 ResolveAttributeValue resAttrValue = tdInfo\r
130                         .getResolveAttributeValue();\r
131                 if (resAttrValue != null)\r
132                 {\r
133                     String attributeName = resAttrValue.getAttributeName();\r
134                     if (attributeName != null && attributeName.length() > 0)\r
135                     {\r
136                         resolveAttributeValue(dtTagConverter.getHostElement(),\r
137                                 dtTagConverter.getResultElement(),\r
138                                 attributeName, dtInfo, tagDecorateInfoID);\r
139                     }\r
140                 }\r
141             }\r
142         }\r
143     }\r
144 \r
145     /**\r
146      * Creates a visual representation result Element for an unknown tag.\r
147      * \r
148      * @param dtTagConverter\r
149      *            DTTagConverter instance.\r
150      */\r
151     protected void createUnknownTagRepresentation(DTTagConverter dtTagConverter)\r
152     {\r
153         Element element = dtTagConverter.createElement("span"); //$NON-NLS-1$\r
154         element.setAttribute("style", "color:red;font-weight:bold;"); //$NON-NLS-1$ //$NON-NLS-2$\r
155         Text text = dtTagConverter\r
156                 .createText("<" + dtTagConverter.getHostElement().getTagName() + "/>"); //$NON-NLS-1$ //$NON-NLS-2$\r
157         element.appendChild(text);\r
158         dtTagConverter.setResultElement(element);\r
159         dtTagConverter.setWidget(true);\r
160     }\r
161 \r
162     /**\r
163      * Adds child Elements of the specified source Element to the specified\r
164      * DTTagConverter instance's collection of non-visual children.\r
165      * \r
166      * @param dtTagConverter\r
167      *            DTTagConverter instance.\r
168      * @param srcElement\r
169      *            Source Element for which child Elements are to be added.\r
170      */\r
171     protected void setNonVisualChildElements(DTTagConverter dtTagConverter,\r
172             Element srcElement)\r
173     {\r
174         NodeList childNodes = srcElement.getChildNodes();\r
175         for (int i = 0; i < childNodes.getLength(); i++)\r
176         {\r
177             Node curNode = childNodes.item(i);\r
178             if (curNode.getNodeType() == Node.ELEMENT_NODE)\r
179             {\r
180                 dtTagConverter.addNonVisualChildElement((Element) curNode);\r
181             }\r
182         }\r
183     }\r
184 \r
185     /**\r
186      * Performs simple EL resolution for the child Text Node of the specified\r
187      * source Element instance.\r
188      * \r
189      * @param srcElement\r
190      *            Source Element for which child Text Node EL resolution is to\r
191      *            be performed.\r
192      * @param dtInfo\r
193      *            IDTInfo instance.\r
194      */\r
195     protected void resolveChildText(Element srcElement, IDTInfo dtInfo)\r
196     {\r
197         if (srcElement != null)\r
198         {\r
199             NodeList childNodes = srcElement.getChildNodes();\r
200             for (int i = 0; i < childNodes.getLength(); i++)\r
201             {\r
202                 Node childNode = childNodes.item(i);\r
203                 if (childNode.getNodeType() == Node.TEXT_NODE)\r
204                 {\r
205                     Text textNode = (Text) childNode;\r
206                     String textNodeValue = textNode.getNodeValue();\r
207                     try\r
208                     {\r
209                         String newTextNodeValue;\r
210                         if (textNodeValue.contains(MD_PLUGIN_LOCATION))\r
211                         {\r
212                             newTextNodeValue = resolveMDPluginLocation(\r
213                                     textNodeValue, dtInfo);\r
214                         } else\r
215                         {\r
216                                 //Bug 319317 - Third-party plug-in providing javax.servlet.jsp.el version 2.1 or greater breaks WPE preview\r
217                                 Map options = new HashMap();\r
218                                 options.put("ELEMENT", srcElement); //$NON-NLS-1$\r
219                             newTextNodeValue = (String) PageExpressionContext\r
220                                     .getCurrent().evaluateExpression(\r
221                                             textNodeValue, String.class, options);\r
222                         }\r
223                         if (newTextNodeValue != null\r
224                                 && !textNodeValue.equals(newTextNodeValue))\r
225                         {\r
226                             textNode.setNodeValue(newTextNodeValue);\r
227                         }\r
228                     } catch (Exception ex)\r
229                     {\r
230                         // ignore - could not resolve, do not change existing\r
231                         // value\r
232                     }\r
233                 }\r
234             }\r
235         }\r
236     }\r
237 \r
238     /**\r
239      * Performs simple EL resolution for the value of the specified attribute of\r
240      * the specified Element.\r
241      * \r
242      * @param srcElement\r
243      * \r
244      * @param targetElement\r
245      *            Source Element instance.\r
246      * @param attributeName\r
247      *            Name of attribute for which the value should be resolved.\r
248      * @param dtInfo\r
249      *            IDTInfo instance.\r
250      * @param tagDecorateInfoID \r
251      */\r
252     protected void resolveAttributeValue(Element srcElement,\r
253             Element targetElement, String attributeName, IDTInfo dtInfo, \r
254             String tagDecorateInfoID)\r
255     {\r
256         if (targetElement != null)\r
257         {\r
258             String oldAttributeValue = null;\r
259             String targetAttributeName = attributeName;\r
260             // determine if attributeName is XPath and re-target as appropriate\r
261             if (attributeName.contains("/")) { //$NON-NLS-1$\r
262                 int lastSlashPos = attributeName.lastIndexOf("/"); //$NON-NLS-1$\r
263                 String xPathExpression = attributeName.substring(0,\r
264                         lastSlashPos);\r
265                 XPath xPath = XPathFactory.newInstance().newXPath();\r
266                 try\r
267                 {\r
268                     Object resultObject = xPath.evaluate(xPathExpression,\r
269                             targetElement, XPathConstants.NODE);\r
270                     if (resultObject instanceof Element)\r
271                     {\r
272                         targetElement = (Element) resultObject;\r
273                         targetAttributeName = attributeName\r
274                                 .substring(lastSlashPos + 1);\r
275                     }\r
276                     else if (resultObject instanceof Text)\r
277                     {\r
278                         Node parentNode = ((Text)resultObject).getParentNode();\r
279                         if (parentNode instanceof Element)\r
280                         {\r
281                             parentNode.normalize();\r
282                             targetAttributeName = IAttributeValueResolver.TEXT_NODE_KEY;\r
283                             oldAttributeValue = ((Text)resultObject).getNodeValue();\r
284                         }\r
285                     }\r
286                 }\r
287                 catch (XPathExpressionException xee)\r
288                 {\r
289                     // could not evaluate - leave targetElement and\r
290                     // targetAttributeName unchanged\r
291                 }\r
292             }\r
293 \r
294             \r
295             if (!IAttributeValueResolver.TEXT_NODE_KEY.equals(targetAttributeName))\r
296             {\r
297                 oldAttributeValue = targetElement.getAttribute(targetAttributeName);\r
298             }\r
299 \r
300             if (oldAttributeValue != null && oldAttributeValue.length() > 0)\r
301             {\r
302                 String newAttributeValue;\r
303                 if (oldAttributeValue.contains(MD_PLUGIN_LOCATION))\r
304                 {\r
305                     newAttributeValue = resolveMDPluginLocation(\r
306                             oldAttributeValue, dtInfo);\r
307                 }\r
308                 else\r
309                 {\r
310                     newAttributeValue = resolveAttributeValue(srcElement,\r
311                             targetElement, targetAttributeName,\r
312                             oldAttributeValue, tagDecorateInfoID);\r
313                 }\r
314                 if (newAttributeValue != null\r
315                         && !oldAttributeValue.equals(newAttributeValue))\r
316                 {\r
317                     if (IAttributeValueResolver.TEXT_NODE_KEY\r
318                             .equals(targetAttributeName))\r
319                     {\r
320                         for (int i = targetElement.getChildNodes().getLength()-1; i >= 0; i--)\r
321                         {\r
322                             Node childNode = targetElement.getChildNodes().item(i);\r
323                             if (childNode.getNodeType() == Node.TEXT_NODE)\r
324                             {\r
325                                 targetElement.removeChild(childNode);\r
326                             }\r
327                         }\r
328                         targetElement.appendChild(targetElement.getOwnerDocument().createTextNode(newAttributeValue));\r
329                     }\r
330                     else\r
331                     {\r
332                         targetElement.setAttribute(targetAttributeName,\r
333                                 newAttributeValue);\r
334                     }\r
335                 }\r
336             }\r
337         }\r
338     }\r
339 \r
340     private String resolveAttributeValue(final Element originalElement,\r
341             final Element convertedElement,\r
342             final String convertedAttributeName, final String oldAttributeValue,\r
343             final String tagDecorateInfoID)\r
344     {\r
345         String newValue = null;\r
346         boolean valueResolved = false;\r
347         final String[] result = new String[1];\r
348 \r
349         for (final IAttributeValueResolver resolver : AttributeValueResolverRegistryReader\r
350                 .getInstance().getExtensions())\r
351         {\r
352             SafeRunner.run(new ISafeRunnable()\r
353             {\r
354                 public void handleException(Throwable exception)\r
355                 {\r
356                     PDPlugin\r
357                             .log(\r
358                                     "While resolving attribute in converter decorator", exception); //$NON-NLS-1$\r
359                 }\r
360 \r
361                 public void run() throws Exception\r
362                 {\r
363                     if (resolver.canResolve(originalElement,\r
364                             convertedElement, convertedAttributeName, oldAttributeValue))\r
365                     {\r
366                         result[0] = resolver.resolveAttribute(\r
367                                 originalElement, convertedElement,\r
368                                 convertedAttributeName, oldAttributeValue);\r
369                     }\r
370                 }\r
371             });\r
372             if (result[0] != null)\r
373             {\r
374                 newValue = result[0];\r
375                 valueResolved = true;\r
376                 break;\r
377             }\r
378         }\r
379 \r
380         if (!valueResolved &&\r
381                 // maintain backward compatibility: only do this default\r
382                 // behaviour for the preview\r
383                 tagDecorateInfoID.equals(DECORATE_INFO_ID_PREVIEW))\r
384         {\r
385             // fall- through to default case.\r
386             try\r
387             {\r
388                 \r
389                 PageExpressionContext current = PageExpressionContext.getCurrent();\r
390                 if (current != null)\r
391                 {\r
392                         //Bug 319317 - Third-party plug-in providing javax.servlet.jsp.el version 2.1 or greater breaks WPE preview\r
393                         Map options = new HashMap();\r
394                         options.put("ELEMENT", originalElement); //$NON-NLS-1$\r
395                     return (String) current\r
396                             .evaluateExpression(oldAttributeValue, String.class, options);\r
397                 }\r
398             } catch (ELException e)\r
399             {\r
400                 // ignore. we will just return null since couldn't resolve\r
401             }\r
402         }\r
403         return newValue;\r
404     }\r
405 \r
406     /**\r
407      * Resolves any instance of MD_PLUGIN_LOCATION in input String.\r
408      * \r
409      * @param input\r
410      *            Input String.\r
411      * @param dtInfo\r
412      *            IDTInfo instance.\r
413      * @return Input String with any instance of MD_PLUGIN_LOCATION resolved.\r
414      */\r
415     protected String resolveMDPluginLocation(String input, IDTInfo dtInfo)\r
416     {\r
417         String output = input;\r
418         if (input != null && input.contains(MD_PLUGIN_LOCATION))\r
419         {\r
420             int tokenStart = input.indexOf(MD_PLUGIN_LOCATION);\r
421             int tokenEnd = tokenStart + MD_PLUGIN_LOCATION.length();\r
422             String prefix = input.substring(0, tokenStart);\r
423             String suffix = input.substring(tokenEnd);\r
424             Trait trait = dtInfo.getTrait();\r
425             IMetaDataSourceModelProvider mdSourceModelProvider = trait\r
426                     .getSourceModelProvider();\r
427             IResourceURLProvider resourceURLProvider = (IResourceURLProvider) mdSourceModelProvider\r
428                     .getAdapter(IResourceURLProvider.class);\r
429             URL url = resourceURLProvider.getResourceURL("/META-INF/"); //$NON-NLS-1$\r
430             String resolvedToken = url.toExternalForm();\r
431             resolvedToken = resolvedToken.substring(0,\r
432                     resolvedToken.length() - 10);\r
433             output = prefix + resolvedToken + suffix;\r
434         }\r
435         return output;\r
436     }\r
437 \r
438     /**\r
439      * Sets DTTagConverter instance as non-visual as HTML and sets the\r
440      * ImageDescriptor instance that DTTagConverter will use to return an Image\r
441      * for rendering.\r
442      * \r
443      * @param dtTagConverter\r
444      *            DTTagConverter instance.\r
445      * @param dtInfo\r
446      *            IDTInfo instance.\r
447      * @param imagePath\r
448      *            Image path, relative to declaring plug-in.\r
449      */\r
450     protected void setNonVisual(DTTagConverter dtTagConverter, IDTInfo dtInfo,\r
451             String imagePath)\r
452     {\r
453         dtTagConverter.setVisualByHTML(false);\r
454         if (imagePath != null && imagePath.length() > 0)\r
455         {\r
456             Trait trait = dtInfo.getTrait();\r
457             IImageDescriptorProvider imgDescProvider = (IImageDescriptorProvider) trait\r
458                     .getSourceModelProvider().getAdapter(\r
459                             IImageDescriptorProvider.class);\r
460             if (imgDescProvider != null)\r
461             {\r
462                 ImageDescriptor imageDescriptor = imgDescProvider\r
463                         .getImageDescriptor(imagePath);\r
464                 dtTagConverter.setVisualImageDescriptor(imageDescriptor);\r
465             }\r
466         }\r
467     }\r
468 \r
469 }\r