/*
 * Copyright 2013, Emanuel Rabina (http://www.ultraq.net.nz/)
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package nz.net.ultraq.thymeleaf.decorator;

import org.thymeleaf.dom.Attribute;
import org.thymeleaf.dom.Element;
import org.thymeleaf.standard.StandardDialect;
import org.thymeleaf.standard.processor.attr.StandardWithAttrProcessor;

/**
 * Common decorator-specific utility methods.
 * 
 * @author Emanuel Rabina
 */
public final class DecoratorUtilities {

	private static final String TH_WITH      = StandardDialect.PREFIX + ":" + StandardWithAttrProcessor.ATTR_NAME;
	private static final String TH_WITH_DATA = "data-" + StandardDialect.PREFIX + "-" + StandardWithAttrProcessor.ATTR_NAME;

	public static final String LINE_SEPARATOR     = System.getProperty("line.separator");
	public static final String HTML_ELEMENT_HTML  = "html";
	public static final String HTML_ELEMENT_HEAD  = "head";
	public static final String HTML_ELEMENT_TITLE = "title";
	public static final String HTML_ELEMENT_BODY  = "body";

	/**
	 * Default hidden constructor as this class is only ever meant to be used
	 * statically.
	 */
	private DecoratorUtilities() {
	}

	/**
	 * Recursive search for an element within the given node in the DOM tree.
	 * 
	 * @param element Node to initiate the search from.
	 * @param name	  Name of the element to look for.
	 * @return Element with the given name, or <tt>null</tt> if the element
	 * 		   could not be found.
	 */
	public static Element findElement(Element element, String name) {

		if (element.getOriginalName().equals(name)) {
			return element;
		}
		for (Element child: element.getElementChildren()) {
			Element result = findElement(child, name);
			if (result != null) {
				return result;
			}
		}
		return null;
	}

	/**
	 * Replace the attributes of the source element with those of the target
	 * element.  A merge is done on the <tt>th:with</tt> attribute, while all
	 * other attributes are simply overidden.
	 * 
	 * @param targetelement
	 * @param sourceelement
	 */
	public static void pullAttributes(Element targetelement, Element sourceelement) {

		if (targetelement == null || sourceelement == null) {
			return;
		}

		for (Attribute contentattribute: sourceelement.getAttributeMap().values()) {
			String attributename = contentattribute.getOriginalName();

			// Merge th:with attributes to retain local variable declarations
			if (attributename.equals(TH_WITH) || attributename.equals(TH_WITH_DATA)) {
				targetelement.setAttribute(TH_WITH, contentattribute.getValue() + "," + (
						targetelement.hasAttribute(TH_WITH)      ? targetelement.getAttributeValue(TH_WITH) :
						targetelement.hasAttribute(TH_WITH_DATA) ? targetelement.getAttributeValue(TH_WITH_DATA) :
						null));
			}
			else {
				targetelement.setAttribute(contentattribute.getOriginalName(), contentattribute.getValue());
			}
		}
	}

	/**
	 * Replace the target element with the source element.
	 * 
	 * @param targettelement
	 * @param sourceelement
	 */
	public static void pullContent(Element targettelement, Element sourceelement) {

		// Clone target element without processing information to make Thymeleaf reprocesses it
		targettelement.clearChildren();
		targettelement.addChild(sourceelement.cloneNode(null, false));
		targettelement.getParent().extractChild(targettelement);
	}
}

@@ -3076,81 +3076,32 @@
 %09%09%09%09
-targetelement.setAttribute(TH_WITH, contentattribute.getValue() + %22,%22 + (
+String targetwithvalue =
 %0D%0A%09%09
@@ -3303,30 +3303,48 @@
 null
-))
 ;%0D%0A%09%09%09
-%7D%0D%0A%09%09%09else
+%09if (targetwithvalue != null)
  %7B%0D%0A
+%09
 %09%09%09%09
@@ -3370,16 +3370,25 @@
 tribute(
+TH_WITH, 
 contenta
@@ -3399,30 +3399,123 @@
 bute.get
-OriginalName()
+Value() + %22,%22 + targetwithvalue);%0D%0A%09%09%09%09%09continue;%0D%0A%09%09%09%09%7D%0D%0A%09%09%09%7D%0D%0A%09%09%09targetelement.setAttribute(attributename
 , conten
@@ -3539,22 +3539,16 @@
 ue());%0D%0A
-%09%09%09%7D%0D%0A
 %09%09%7D%0D%0A%09%7D%0D

Fix attribute merging.