All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.wicket.markup.resolver.AutoComponentResolver Maven / Gradle / Ivy

Go to download

Pax Wicket Service is an OSGi extension of the Wicket framework, allowing for dynamic loading and unloading of Wicket components and pageSources.

There is a newer version: 5.0.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.wicket.markup.resolver;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

import org.apache.wicket.Application;
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.MarkupException;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.WicketTag;
import org.apache.wicket.markup.html.border.Border;
import org.apache.wicket.markup.parser.filter.WicketTagIdentifier;
import org.apache.wicket.util.convert.IConverter;


/**
 * <wicket:component class="myApp.MyTable" key=value> tags may be used to add Wicket
 * components (e.g. a specialized PageableListView) and pass parameters (e.g. the number of rows per
 * list view page). The object is automatically instantiated, initialized and added to the page's
 * component hierarchy.
 * 

* Note: The component must have a constructor with a single String parameter: the component name. *

* Note: The component must provide a setter for each key/value attribute provided. * * @author Juergen Donnerstag */ public final class AutoComponentResolver implements IComponentResolver { private static final long serialVersionUID = 1L; static { // register "wicket:component" WicketTagIdentifier.registerWellKnownTagName("component"); } /** * Temporary storage for containers currently being rendered. Thus child components can be * re-parented. Remember: are an exception to the rule. Though the markup of * the children are nested inside , their respective Java components are not. * They must be added to the parent container of . */ private final Map nestedComponents = new HashMap(); /** * @see org.apache.wicket.markup.resolver.IComponentResolver#resolve(MarkupContainer, * MarkupStream, ComponentTag) * @param container * The container parsing its markup * @param markupStream * The current markupStream * @param tag * The current component tag while parsing the markup * @return true, if componentId was handle by the resolver. False, otherwise */ public final boolean resolve(final MarkupContainer container, final MarkupStream markupStream, final ComponentTag tag) { // It must be if (tag instanceof WicketTag) { // It must be final WicketTag wicketTag = (WicketTag)tag; if (wicketTag.isComponentTag()) { // Create and initialize the component final Component component = createComponent(container, wicketTag); if (component != null) { // 1. push the current component onto the stack if (component instanceof Border) { nestedComponents.put(((Border)component).getBodyContainer(), container); } else { nestedComponents.put(component, container); } try { // 2. Add it to the hierarchy and render it container.autoAdd(component, markupStream); } finally { // 3. remove it from the stack nestedComponents.remove(component); } return true; } } } // Re-parent children of . if ((tag.getId() != null) && nestedComponents.containsKey(container)) { // Make sure you handle nested auto-components properly MarkupContainer parent = nestedComponents.get(container); while (parent != null) { Component component = parent.get(tag.getId()); if (component != null) { component.render(markupStream); return true; } parent = parent.getParent(); if (nestedComponents.containsKey(parent)) { parent = nestedComponents.get(parent); } } } // We were not able to handle the componentId return false; } /** * Based on the tag, create and initialize the component. * * @param container * The current container. The new component will be added to that container. * @param tag * The tag containing the information about component * @return The new component * @throws WicketRuntimeException * in case the component could not be created */ // Wicket is current not using any bean util jar, which is why ... private final Component createComponent(final MarkupContainer container, final WicketTag tag) { // If no component name is given, create a page-unique one yourself. String componentId = tag.getNameAttribute(); if (componentId == null) { componentId = "anonymous-" + container.getPage().getAutoIndex(); } // Get the component class name final String classname = tag.getAttributes().getString("class"); if ((classname == null) || (classname.trim().length() == 0)) { throw new MarkupException("Tag must have attribute 'class'"); } // construct the component. It must have a constructor with a single // String (componentId) parameter. final Component component; try { // Load the class. In case a Groovy Class Resolver has been provided, // the name might be a Groovy file. // Note: Spring based components are not supported this way. May be we // should provide a ComponentFactory like we provide a PageFactory. final Class componentClass = container.getSession() .getClassResolver() .resolveClass(classname); final Constructor constructor = componentClass.getConstructor(new Class[] { String.class }); component = (Component)constructor.newInstance(new Object[] { componentId }); } catch (ClassNotFoundException e) { throw new MarkupException("Unable to create Component from wicket tag: Cause: " + e.getMessage()); } catch (NoSuchMethodException e) { throw new MarkupException("Unable to create Component from wicket tag: Cause: " + e.getMessage()); } catch (InvocationTargetException e) { throw new MarkupException("Unable to create Component from wicket tag: Cause: " + e.getMessage()); } catch (IllegalAccessException e) { throw new MarkupException("Unable to create Component from wicket tag: Cause: " + e.getMessage()); } catch (InstantiationException e) { throw new MarkupException("Unable to create Component from wicket tag: Cause: " + e.getMessage()); } catch (ClassCastException e) { throw new MarkupException("Unable to create Component from wicket tag: Cause: " + e.getMessage()); } catch (SecurityException e) { throw new MarkupException("Unable to create Component from wicket tag: Cause: " + e.getMessage()); } // Get all remaining attributes and invoke the component's setters Iterator> iter = tag.getAttributes().entrySet().iterator(); while (iter.hasNext()) { final Map.Entry entry = iter.next(); final String key = entry.getKey(); final String value = entry.getValue().toString(); // Ignore attributes 'name' and 'class' if ("name".equalsIgnoreCase(key) || ("class".equalsIgnoreCase(key))) { continue; } invokeSetter(component, key, value, container.getLocale()); } return component; } /** * Invoke the setter method for 'name' on object and provide the 'value' * * @param object * @param name * @param value * @param locale */ private final void invokeSetter(final Object object, final String name, final String value, final Locale locale) { // Note: tag attributes are maintained in a LowerCaseKeyValueMap, thus // 'name' will be all lowercase. // Note: because the attributes are all lowercase, there is slight // possibility of error due to naming issues. // Note: all setters must start with "set" // Get the setter for the attribute final String methodName = "set" + name; final Method[] methods = object.getClass().getMethods(); Method method = null; for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equalsIgnoreCase(methodName)) { method = methods[i]; break; } } if (method == null) { throw new MarkupException("Unable to initialize Component. Method with name " + methodName + " not found"); } // The method must have a single parameter final Class[] parameterClasses = method.getParameterTypes(); if (parameterClasses.length != 1) { throw new MarkupException("Unable to initialize Component. Method with name " + methodName + " must have one and only one parameter"); } // Convert the parameter if necessary, depending on the setter's // attribute final Class paramClass = parameterClasses[0]; try { final IConverter converter = Application.get() .getConverterLocator() .getConverter(paramClass); final Object param = converter.convertToObject(value, locale); if (param == null) { throw new MarkupException("Unable to convert value '" + value + "' into " + paramClass + ". May be there is no converter for that type registered?"); } method.invoke(object, new Object[] { param }); } catch (IllegalAccessException ex) { throw new MarkupException( "Unable to initialize Component. Failure while invoking method " + methodName + ". Cause: " + ex); } catch (InvocationTargetException ex) { throw new MarkupException( "Unable to initialize Component. Failure while invoking method " + methodName + ". Cause: " + ex); } catch (NumberFormatException ex) { throw new MarkupException( "Unable to initialize Component. Failure while invoking method " + methodName + ". Cause: " + ex); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy