Hi Praveen,

Attached is the AutoComponentPanel.java. I've just re-written it so that it implements IComponentResolver. Parts of the code might be too complex and need to be refactored (some parts working with org.w3c.dom), but for now it works. If you have additions or modifications, please let me know. We plan to release this code anyway either as part of a Qti framework, or just this panel as part of some wicket-contrib / wicketstuff project.

Matthijs

PS. Wouter has uploaded his slides to slideshare: http://www.slideshare.net/Func/wicket-dynamic-components

Pen wrote:
Thanks Matthijs,

I got a chance to look at Wouter Huijnink slides. This is what exactly I am
looking. Can you please forward the AutoComponentPanel code. That would be
great.

~Praveen



Matthijs Wensveen-2 wrote:
Hello Praveen,

Wouter Huijnink presented something similar to what you need at the wicket meetup in Amsterdam. We generate html dynamically from xml, using xslt. A component called AutoComponentPanel parses the html and adds components to the hierarchy accordingly. This is actually a two-step process. The first step is telling wicket you want to supply markup yourself instead of letting wicket read it from the corresponding html file. You can do this by implementing IMarkupResourceStreamProvider and if applicable also IMarkupCacheKeyProvider. Then you need to parse the markup stream and add components.

If you're interested I can send the AutoComponentPanel code. It still needs some work to make it shine, but we plan to open source it anyway as part of a wicket-based QTI framework.

PS. Unfortunately Wouter's slides aren't uploaded yet.


Pen wrote:
thanks Johan for your reply. I did take look into your slides.
We can generate HTML pages, we have no issue with it. But how to display
this newly created HTML pages which only exists in memory, there is no
physical file. And also to navigate to this new HTML page.

For example If I create a simple HTML page at runtime like below
test.html.
How to display it and navigate to it. As it also requires corresponding
test.java. This is simple one. But what if we have wicket:Ids we need
construct the Java files with all the action listener also.

test.html
<html>
<body>
   <h1>Hello world! </h1>
</body>
<html>


~Praveen



Johan Compagner wrote:
Generate on one side the html by a servlet or special template
generator, that reads in your db data and generate the component
structure on the other side.

Look at he slides i put on of the presentation that i did for the
wicket user group in the netherlands

2007/12/4, Pen <[EMAIL PROTECTED]>:
I looked into the example wicketstuff-crud, this is basically in memory
database with basic CRUD operation. this is not what exactly I am
looking.
Let me restate the question. We have application where in user can
create
a
webpages using web designer by drag-n-drop where is html elements like
text,
image, selection box, combo box and save it in DB.
It will be Json format parse it to POJO and store in DB. for example
Image
object looks like this
which has got position, style, etc .
[{"position":({left:60, top:40}),
"size":({width:100,height:80}),
"positionTop":40,"positionLeft":60,"sizeWidth":100,"sizeHeight":80,
"cssClass":"",
"style":"left:60px;top:40px;width:100px;height:80px;",
]}}]})

Now we need to read from the DB and reconstruct the same Image object
has
a
html page. We can construct the above object with HTML tags.
But the question is how to display this html pages, since it exists
only
in
memory and also how to navigate to this newly created page.

~Praveen







igor.vaynberg wrote:
see how wicketstuff-crud does it in wicket-stuff svn

-igor


On Dec 3, 2007 6:30 PM, Pen <[EMAIL PROTECTED]> wrote:
I am a new wicker user.  We need to construct/generate  a HTML page
dynamically at runtime from the HTML elements like image and text.
This
page only exists in memory(session/cache) and  there is no physical
file.
so
how to generate such page and corresponding java class. How this can
be
done
for static elements like image and text versus dynamically for form
submit.
Also how to navigate to this newly generated html page.

~Praveen
--
View this message in context:

http://www.nabble.com/How-to-dynamically-generate-HTML-page--tf4940771.html#a14143413
Sent from the Wicket - User mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]



--
View this message in context:
http://www.nabble.com/How-to-dynamically-generate-HTML-page--tf4940771.html#a14156946
Sent from the Wicket - User mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]



--
Matthijs Wensveen
Func. Internet Integration
W http://www.func.nl
T +31 20 4230000
F +31 20 4223500

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]






--
Matthijs Wensveen
Func. Internet Integration
W http://www.func.nl
T +31 20 4230000
F +31 20 4223500
/*
* Copyright 2007 Func. Internet Integration
*
* 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 nl.func.wicket.markup.html.panel;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupElement;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.RawMarkup;
import org.apache.wicket.markup.WicketTag;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Check;
import org.apache.wicket.markup.html.form.CheckBoxMultipleChoice;
import org.apache.wicket.markup.html.form.CheckGroup;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.ListChoice;
import org.apache.wicket.markup.html.form.ListMultipleChoice;
import org.apache.wicket.markup.html.form.Radio;
import org.apache.wicket.markup.html.form.RadioChoice;
import org.apache.wicket.markup.html.form.RadioGroup;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.resolver.IComponentResolver;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * A Panel that automatically adds components to the component hierarchy
 * according to the associated markup. This is done by inspecting the markup
 * stream and adding components for well-known tags. For instance, a TextField
 * is always added to a ComponentTag with name 'input' and a type attribute
 * value 'text'. The wicket:id of the added components are treated as property
 * expressions that are bound to this component's model. For instance,
 * [EMAIL PROTECTED] <input type="text" wicket:id="name"/>}, binds the generated TextField
 * to the model object's 'name' property.
 *
 * Subclasses can override
 *
 * @author matthijs
 *
 */
public class AutoComponentPanel extends Panel implements IComponentResolver{
	private static final long serialVersionUID = 1L;
	private static final Logger logger = LoggerFactory.getLogger(AutoComponentPanel.class);

	private static final String TAG_TYPE_DEFAULT = "text";

	private static DocumentBuilderFactory documentBuilderFactory;

	// initialize and configure the documentBuilderFactory
	static {
		documentBuilderFactory = DocumentBuilderFactory.newInstance();
		documentBuilderFactory.setNamespaceAware(true);
	}

	/**
	 * @param id
	 */
	public AutoComponentPanel(final String id) {
		super(id);
	}

	/**
	 * @param id
	 * @param model
	 */
	public AutoComponentPanel(final String id, final IModel model) {
		super(id, model);
	}

	public boolean resolve(final MarkupContainer container, final MarkupStream markupStream, final ComponentTag tag) {
		final int currentIndex = markupStream.getCurrentIndex();
		final Component component = getMarkupComponent(tag, markupStream);
		markupStream.setCurrentIndex(currentIndex);

		if (component != null) {
			container.autoAdd(component, markupStream);
			return true;
		}

		return false;
	}

	/**
	 * Create and return a Component based on the current componentTag. This already works for most form components. Subclasses should override this if they want to
	 * @param componentTag
	 * @param currentIndex
	 * @param markupStream
	 * @return
	 */
	protected Component getMarkupComponent(final ComponentTag componentTag, final MarkupStream markupStream) {
		if (componentTag instanceof WicketTag || componentTag.isAutoComponentTag()) {
			return null;
		}

		final IModel model = new PropertyModel(getModel(), componentTag.getId());

		if (XHTMLConstants.TAG_INPUT.equals(componentTag.getName())) {
			final String type = componentTag.getAttributes().getString(XHTMLConstants.ATTRIBUTE_TYPE, TAG_TYPE_DEFAULT);
			if (XHTMLConstants.INPUT_TYPE_TEXT.equals(type)) {
				return new TextField(componentTag.getId(), model, String.class);
			} else if (XHTMLConstants.INPUT_TYPE_RADIO.equals(type)) {
				final IModel radioModel = new Model(componentTag.getAttributes().getString(XHTMLConstants.ATTRIBUTE_VALUE));
				return new Radio(componentTag.getId(), radioModel);
			} else if (XHTMLConstants.INPUT_TYPE_CHECKBOX.equals(type)) {
				// Check when there is a CheckGroup, CheckBox when standalone
				// (with a Boolean model object). For now, Check suffices.
				final IModel checkModel = new Model(componentTag.getAttributes().getString(XHTMLConstants.ATTRIBUTE_VALUE));
				return new Check(componentTag.getId(), checkModel);
			} else if (XHTMLConstants.INPUT_TYPE_SUBMIT.equals(type)) {
				return new Button(componentTag.getId(), model);
			}
		} else if (XHTMLConstants.TAG_TEXTAREA.equals(componentTag.getName())) {
			if(componentTag.isClose()) {
				return null;
			}

			return new TextArea(componentTag.getId(), model).setType(String.class);
		} else if (XHTMLConstants.TAG_SELECT.equals(componentTag.getName())) {
			return getSelectMarkupComponent(componentTag, markupStream);
		} else if (componentTag.isOpen()) {
			return getComplexMarkupComponent(componentTag, markupStream);
		}

		// TODO: Button, DropDownChoice,
		// ListMultipleChoice

		return null;
	}

	private Component getComplexMarkupComponent(final ComponentTag componentTag, final MarkupStream markupStream) {
		final IModel model = new PropertyModel(getModel(), componentTag.getId());

		// inspect content to see what kind of Component it is
		Class<? extends WebMarkupContainer> containerClass = null;
		final StringBuilder rawMarkup = new StringBuilder();
		final List<Component> innerComponents = new ArrayList<Component>();
		for (MarkupElement element = markupStream.next(); !element.closes(componentTag); element = markupStream.next()) {
			if (element instanceof ComponentTag) {
				final Component innerComponent = getMarkupComponent((ComponentTag) element, markupStream);
				if (innerComponent != null) {
					innerComponents.add(innerComponent);
				}

				// infer component type
				if (innerComponent instanceof Radio) {
					if (containerClass == null) {
						containerClass = RadioGroup.class;
					} else if (containerClass == CheckGroup.class) {
						// mixed radio and checkbox content, must be WebMarkupContainer
						containerClass = WebMarkupContainer.class;
					}
				} else if (innerComponent instanceof Check) {
					if (containerClass == null) {
						containerClass = CheckGroup.class;
					} else if (containerClass == RadioGroup.class) {
						// mixed radio and checkbox content, must be WebMarkupContainer
						containerClass = WebMarkupContainer.class;
					}
				}
			}

			if (element instanceof RawMarkup) {
				rawMarkup.append(((RawMarkup) element).toString());
			}
		}

		// no inner components, only rawMarkup
		if (!innerComponents.isEmpty()) {
			WebMarkupContainer container;
			if (containerClass == null || containerClass == WebMarkupContainer.class) {
				// neither RadioGroup nor CheckGroup (componentClass ==
				// null), or both RadioGroup and CheckGroup (componentClass
				// = WebMarkupContainer)

				// XXX: model?
				container = new WebMarkupContainer(componentTag.getId(), model);
			} else if (containerClass == RadioGroup.class) {
				container = new RadioGroup(componentTag.getId(), model);
			} else if (containerClass == CheckGroup.class) {
				container = new CheckGroup(componentTag.getId(), model);
			} else {
				// unpossible!
				throw new WicketRuntimeException("Unsuppored component class! (unpossible!)");
			}

			for (final Component innerComponent : innerComponents) {
				container.add(innerComponent);
			}
			return container;
		} else {
			return getComplexMarkupComponentFromRawMarkup(componentTag, rawMarkup.toString());
		}
	}

	private Component getComplexMarkupComponentFromRawMarkup(final ComponentTag componentTag, final String rawMarkup) {
		final IModel model = new PropertyModel(getModel(), componentTag.getId());

		// inspect rawMarkup to infer component type
		try {
			final Node rawMarkupNode = parseRawMarkup(rawMarkup);

			final XPath xPath = XPathFactory.newInstance().newXPath();

			// it's mixedContent when there are inputs other than radio
			// or checkbox or when there are both radios and checkboxes
			if ((Boolean) xPath.evaluate(
							"//input[(@type != 'radio' and @type !='checkbox')] or " +
							"(//[EMAIL PROTECTED] = 'radio'] and //[EMAIL PROTECTED] = 'checkbox'])",
							rawMarkupNode, XPathConstants.BOOLEAN)) {
				// no inputs, mixed content, or ?? -> WebMarkupContainer
				return new WebMarkupContainer(componentTag.getId(), model);
			}

			final List<String> labels = new ArrayList<String>();
			final List<String> values = new ArrayList<String>();
			getValuesLabelsFromInputs(rawMarkupNode, values, labels);

			final ChoiceRenderer choiceRenderer = new ValueLabelChoiceRenderer(labels, values);

			if ((Boolean) xPath.evaluate("//[EMAIL PROTECTED] = 'radio']", rawMarkupNode, XPathConstants.BOOLEAN)) {
				return new RadioChoice(componentTag.getId(), model, Model.valueOf(values), choiceRenderer);
			} else {
				return new CheckBoxMultipleChoice(componentTag.getId(), model, Model.valueOf(values), choiceRenderer);
			}

		} catch (final XPathExpressionException e) {
			logger.error("Could not detect complex Component type", e);
			throw new WicketRuntimeException("Could not detect complex Component type", e);
		}
	}

	private Component getSelectMarkupComponent(final ComponentTag componentTag, final MarkupStream markupStream) {
		final IModel model = new PropertyModel(getModel(), componentTag.getId());

		// get the choices
		final List<String> values = new ArrayList<String>();
		final List<String> labels = new ArrayList<String>();

		final StringBuilder rawMarkup = new StringBuilder();
		for (MarkupElement element = markupStream.next(); !element.closes(componentTag); element = markupStream.next()) {
			if (element instanceof RawMarkup) {
				rawMarkup.append(((RawMarkup) element).toString());
			}
		}

		final Node rawMarkupNode = parseRawMarkup(rawMarkup.toString());
		getValuesLabelsFromSelect(rawMarkupNode, values, labels);

		final ChoiceRenderer choiceRenderer = new ValueLabelChoiceRenderer(labels, values);

		// select the proper component
		if (componentTag.getAttributes().containsKey(XHTMLConstants.ATTRIBUTE_MULTIPLE)) {
			final ListMultipleChoice listMultipleChoice = new ListMultipleChoice(componentTag.getId(), model, values, choiceRenderer);
			if (componentTag.getAttributes().containsKey(XHTMLConstants.ATTRIBUTE_SIZE)) {
				listMultipleChoice.setMaxRows(componentTag.getAttributes().getInt(XHTMLConstants.ATTRIBUTE_SIZE));
			}
			return listMultipleChoice;
		} else if (componentTag.getAttributes().containsKey(XHTMLConstants.ATTRIBUTE_SIZE)) {
			return new ListChoice(componentTag.getId(), model, values, choiceRenderer)
						.setMaxRows(componentTag.getAttributes().getInt(XHTMLConstants.ATTRIBUTE_SIZE));
		} else {
			return new DropDownChoice(componentTag.getId(), model, values, choiceRenderer);
		}
	}

	private void getValuesLabelsFromInputs(final Node node, final List<String> values, final List<String> labels) {
		final XPath xPath = XPathFactory.newInstance().newXPath();
		try {
			// select all radios and checkboxes (either one or it would be mixedContent)
			final NodeList inputNodes = (NodeList) xPath.evaluate(
					"//[EMAIL PROTECTED] = 'radio' or @type = 'checkbox']",
					node, XPathConstants.NODESET);

			if (inputNodes != null && inputNodes.getLength() > 0) { // else: no radios or checkboxes at all so skip it

				final int length = inputNodes.getLength();
				for (int ii = 0; ii < length; ii++) {
					final Node inputNode = inputNodes.item(ii);

					final Node value = (Node) xPath.evaluate("@value", inputNode, XPathConstants.NODE);
					if (value == null) {
						// input does not have a value, radios and
						// checkboxes MUST have a value attribute:
						// http://www.w3.org/TR/html4/interact/forms.html#adef-value-INPUT
						continue;
					}

					Node label = null;
					// 1. <label><input value="value"/>label</label>
					label = (Node) xPath.evaluate("ancestor::label", inputNode, XPathConstants.NODE);
					if (label == null) {
						final String id = (String) xPath.evaluate("@id", inputNode, XPathConstants.STRING);
						if (id != null) {
							// 2. <label for="id">label</label> .. <input id="id" value="value"/>
							label = (Node) xPath.evaluate("//[EMAIL PROTECTED] = '" + id + "']", node, XPathConstants.NODE);
						}
					}

					if (label == null) {
						// 3. <input value="value"/> without label
						label = value;
					}

					labels.add(label.getTextContent());
					values.add(value.getTextContent());
				}
			}
		} catch (final XPathExpressionException e) {
			logger.error("Could not get the labels or values",e);
			throw new WicketRuntimeException("Could not get the labels or values", e);
		}

	}

	private void getValuesLabelsFromSelect(final Node node, final List<String> values, final List<String> labels) {
		final XPath xPath = XPathFactory.newInstance().newXPath();

		// select all radios and checkboxes (either one or it would be mixedContent)
		NodeList optionNodes;
		try {
			optionNodes = (NodeList) xPath.evaluate(
							"//option",
							node, XPathConstants.NODESET);

			if (optionNodes != null && optionNodes.getLength() > 0) { // else: no radios or checkboxes at all so skip it

				final int length = optionNodes.getLength();
				for (int ii = 0; ii < length; ii++) {
					final Node inputNode = optionNodes.item(ii);

					// <option (value="value")?>label</option>
					final String label = (String) xPath.evaluate("text()", inputNode, XPathConstants.STRING);

					String value = (String) xPath.evaluate("@value", inputNode, XPathConstants.STRING);
					if (value == null) {
						value = label;
					}

					labels.add(label);
					values.add(value);
				}
			}
		} catch (final XPathExpressionException e) {
			logger.error("Could not get the labels or values",e);
			throw new WicketRuntimeException("Could not get the labels or values", e);
		}
	}

	private Node parseRawMarkup(final String rawMarkup) {
		try {
			final Document document = documentBuilderFactory.newDocumentBuilder().
				parse(new InputSource(new StringReader("<root>" + rawMarkup.toString() + "</root>")));

			return document.getDocumentElement();
		} catch (final SAXException e) {
			throw new WicketRuntimeException("Exception while parsing raw markup!", e);
		} catch (final IOException e) {
			throw new WicketRuntimeException("Exception while parsing raw markup!", e);
		} catch (final ParserConfigurationException e) {
			throw new WicketRuntimeException("Exception while parsing raw markup!", e);
		}
	}

	private static class ValueLabelChoiceRenderer extends ChoiceRenderer {
		private static final long serialVersionUID = 1L;

		private final Map<String, String> valueLabelMap;

		public ValueLabelChoiceRenderer(final List<String> labels, final List<String> values) {
			valueLabelMap = new HashMap<String, String>(values.size());

			for (int ii = 0; ii < values.size(); ii++) {
				if (ii < labels.size()) {
					valueLabelMap.put(values.get(ii), labels.get(ii));
				} else {
					valueLabelMap.put(values.get(ii), values.get(ii));
				}
			}
		}

		@Override
		public Object getDisplayValue(final Object object) {
			return valueLabelMap.get(object);
		}

		@Override
		public String getIdValue(final Object object, final int index) {
			return (String) object;
		}

	}
}


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to