r1144934 fixed the inconsistency thanks! On Sun, Jul 10, 2011 at 6:14 PM, Igor Vaynberg <[email protected]> wrote: > because AutoLabel handles the label text output/input in a completely > different way and i didnt want to break backwards compat this late in > the game. > > -igor > > > On Sun, Jul 10, 2011 at 3:29 AM, Martin Grigorov <[email protected]> wrote: >> Hi, >> >> Why did you prefer to introduce AutoLabel instead of using >> re-FormComponentLabel ? >> I see the new one has some more logic about setting required/valid classes. >> The diff I notice in FormComponentLabel is that it accepts >> LabeledWebMarkupContainer instead of FormComponent as ctor parameter. >> This allows it to be used with Check, Radio and CheckGroupSelector >> additionally. >> >> On Sat, Jul 9, 2011 at 6:15 AM, <[email protected]> wrote: >>> Author: ivaynberg >>> Date: Sat Jul 9 04:15:13 2011 >>> New Revision: 1144589 >>> >>> URL: http://svn.apache.org/viewvc?rev=1144589&view=rev >>> Log: >>> wicket:for attribute to make it easy to link form component labels to form >>> components >>> Issue: WICKET-1469 >>> >>> Added: >>> >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelResolver.java >>> (with props) >>> >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelTagHandler.java >>> (with props) >>> >>> wicket/branches/wicket-1.4.x/wicket/src/test/java/org/apache/wicket/markup/html/form/AutoLabelTest.java >>> (with props) >>> Modified: >>> >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/MarkupParser.java >>> >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/protocol/http/WebApplication.java >>> >>> Modified: >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/MarkupParser.java >>> URL: >>> http://svn.apache.org/viewvc/wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/MarkupParser.java?rev=1144589&r1=1144588&r2=1144589&view=diff >>> ============================================================================== >>> --- >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/MarkupParser.java >>> (original) >>> +++ >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/MarkupParser.java >>> Sat Jul 9 04:15:13 2011 >>> @@ -24,6 +24,7 @@ import java.util.regex.Pattern; >>> import org.apache.wicket.Application; >>> import org.apache.wicket.Page; >>> import org.apache.wicket.WicketRuntimeException; >>> +import org.apache.wicket.markup.html.form.AutoLabelTagHandler; >>> import org.apache.wicket.markup.parser.IMarkupFilter; >>> import org.apache.wicket.markup.parser.IXmlPullParser; >>> import org.apache.wicket.markup.parser.XmlPullParser; >>> @@ -163,6 +164,7 @@ public class MarkupParser >>> appendMarkupFilter(new WicketLinkTagHandler()); >>> appendMarkupFilter(new >>> WicketNamespaceHandler(markupResourceData)); >>> >>> + >>> // Provided the wicket component requesting the markup is >>> known ... >>> final MarkupResourceStream resource = >>> markupResourceData.getResource(); >>> if (resource != null) >>> @@ -186,6 +188,7 @@ public class MarkupParser >>> appendMarkupFilter(new RelativePathPrefixHandler()); >>> appendMarkupFilter(new EnclosureHandler()); >>> appendMarkupFilter(new InlineEnclosureHandler()); >>> + appendMarkupFilter(new AutoLabelTagHandler()); >>> } >>> >>> /** >>> >>> Added: >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelResolver.java >>> URL: >>> http://svn.apache.org/viewvc/wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelResolver.java?rev=1144589&view=auto >>> ============================================================================== >>> --- >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelResolver.java >>> (added) >>> +++ >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelResolver.java >>> Sat Jul 9 04:15:13 2011 >>> @@ -0,0 +1,372 @@ >>> +package org.apache.wicket.markup.html.form; >>> + >>> +import org.apache.wicket.Component; >>> +import org.apache.wicket.Component.IVisitor; >>> +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.html.WebMarkupContainer; >>> +import org.apache.wicket.markup.parser.XmlPullParser; >>> +import org.apache.wicket.markup.parser.XmlTag; >>> +import org.apache.wicket.markup.resolver.IComponentResolver; >>> +import org.apache.wicket.model.Model; >>> +import org.apache.wicket.util.string.AppendingStringBuffer; >>> +import org.apache.wicket.util.string.Strings; >>> +import org.slf4j.Logger; >>> +import org.slf4j.LoggerFactory; >>> + >>> +/** >>> + * Resolver that implements the {@code wicket:for} attribute >>> functionality. The attribute makes it >>> + * easy to set up {@code <label>} tags for form components by providing >>> the following features >>> + * without having to add any additional components in code: >>> + * <ul> >>> + * <li>Outputs the {@code for} attribute with the value equivalent to the >>> markup id of the >>> + * referenced form component</li> >>> + * <li>Appends {@code required} css class to the {@code <label>} tag if >>> the referenced form >>> + * component is required</li> >>> + * <li>Appends {@code error} css class to the {@code <label>} tag if the >>> referenced form component >>> + * has failed validation</li> >>> + * <li>If the {@code <label>} tag contains {@code <span >>> class='text'></span>} markup and the form >>> + * component has a label configured either via the label model or a >>> property files, the body of the >>> + * {code <span>} will be replaced with the label</li> >>> + * <li>If the {@code <label>} tag contains {@code <span >>> class='text'>body</span>} markup and the >>> + * form component does not have a label configured either via the label >>> model or a properties file, >>> + * the label of the form component will be set to the body of the {@code >>> <span>} tag - in this >>> + * example {@code body}</li> >>> + * </ul> >>> + * >>> + * <p> >>> + * The value of the {@code wicket:for} atribute can either contain an id >>> of the form component or a >>> + * path a path to it using the standard {@code :} path separator. Note >>> that {@code ..} can be used >>> + * as part of the path to construct a reference to the parent container, >>> eg {@code ..:..:foo:bar}. >>> + * First the value of the attribute will be treated as a path and the >>> {@code <label>} tag's closest >>> + * parent container will be queried for the form component. If the form >>> component cannot be resolved >>> + * the value of the {@code wicket:for} attribute will be treated as an id >>> and all containers will be >>> + * searched from the closest parent to the page. >>> + * </p> >>> + * >>> + * <p> >>> + * Given markup like this: >>> + * >>> + * <code> >>> + * [label wicket:for="name"][span class="text"]Name[/span]:[/label][input >>> wicket:id="name" type="text"/] >>> + * </code> >>> + * >>> + * If the {@code name} component has its label set to 'First Name' the >>> resulting output will be: >>> + * <code> >>> + * [label for="name5"][span class="text"]First Name[/span]:[/label][input >>> name="name" type="text" id="name5"/] >>> + * </code> >>> + * >>> + * However, if the {@code name} component does not have a label set then >>> it will be set to >>> + * {@code Name} based on the markup. >>> + * </p> >>> + * >>> + * @author igor >>> + */ >>> +public class AutoLabelResolver implements IComponentResolver >>> +{ >>> + private static Logger logger = >>> LoggerFactory.getLogger(AutoLabelResolver.class); >>> + >>> + public boolean resolve(MarkupContainer container, MarkupStream >>> markupStream, ComponentTag tag) >>> + { >>> + if >>> (!AutoLabelTagHandler.class.getName().equals(tag.getId())) >>> + { >>> + return false; >>> + } >>> + >>> + final String id = tag.getAttribute("wicket:for").trim(); >>> + >>> + FormComponent<?> component = >>> findRelatedComponent(container, id); >>> + >>> + if (component == null) >>> + { >>> + throw new WicketRuntimeException("Could not find >>> form component with id: " + id + >>> + " while trying to resolve wicket:for >>> attribute"); >>> + } >>> + if (!(component instanceof FormComponent<?>)) >>> + { >>> + throw new WicketRuntimeException("Component pointed >>> to by wicket:for attribute: " + id + >>> + " is not a form component"); >>> + } >>> + >>> + if (!component.getOutputMarkupId()) >>> + { >>> + component.setOutputMarkupId(true); >>> + if (!component.hasBeenRendered()) >>> + { >>> + logger.warn( >>> + "Form component: {} is reference >>> via a wicket:for attribute but does not have its outputMarkupId property >>> set to true", >>> + component.toString(false)); >>> + } >>> + } >>> + final FormComponent<?> fc = component; >>> + >>> + container.autoAdd(new AutoLabel("label" + >>> container.getPage().getAutoIndex2(), fc), >>> + markupStream); >>> + >>> + return true; >>> + } >>> + >>> + protected FormComponent<?> findRelatedComponent(MarkupContainer >>> container, final String id) >>> + { >>> + // try the quick and easy route first >>> + >>> + Component component = container.get(id); >>> + if (component != null && (component instanceof >>> FormComponent<?>)) >>> + { >>> + return (FormComponent<?>)component; >>> + } >>> + >>> + // try the long way, search the hierarchy from the closest >>> container up to the page >>> + >>> + final Component[] searched = new Component[] { null }; >>> + while (container != null) >>> + { >>> + component = >>> (Component)container.visitChildren(Component.class, >>> + new IVisitor<Component>() >>> + { >>> + public Object component(Component >>> child) >>> + { >>> + if (child == searched[0]) >>> + { >>> + // this container >>> was already searched >>> + return >>> CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER; >>> + } >>> + if >>> (id.equals(child.getId()) && (child instanceof FormComponent)) >>> + { >>> + return child; >>> + } >>> + return CONTINUE_TRAVERSAL; >>> + } >>> + }); >>> + >>> + if (component != null && (component instanceof >>> FormComponent)) >>> + { >>> + return (FormComponent<?>)component; >>> + } >>> + >>> + // remember the container so we dont search it >>> again, and search the parent >>> + searched[0] = container; >>> + container = container.getParent(); >>> + } >>> + >>> + return null; >>> + } >>> + >>> + /** >>> + * Component that is attached to the {@code <label>} tag and takes >>> care of writing out the label >>> + * text as well as setting classes on the {@code <label>} tag >>> + * >>> + * @author igor >>> + */ >>> + protected static class AutoLabel extends WebMarkupContainer >>> + { >>> + private final FormComponent<?> fc; >>> + >>> + public AutoLabel(String id, FormComponent<?> fc) >>> + { >>> + super(id); >>> + this.fc = fc; >>> + } >>> + >>> + @Override >>> + protected void onComponentTag(ComponentTag tag) >>> + { >>> + super.onComponentTag(tag); >>> + tag.put("for", fc.getMarkupId()); >>> + if (fc.isRequired()) >>> + { >>> + tag.append("class", "required", " "); >>> + } >>> + if (!fc.isValid()) >>> + { >>> + tag.append("class", "error", " "); >>> + } >>> + } >>> + >>> + @Override >>> + protected void onComponentTagBody(MarkupStream >>> markupStream, ComponentTag openTag) >>> + { >>> + if (!(markupStream.get() instanceof RawMarkup)) >>> + { >>> + // no raw markup found inside the label, do >>> not modify the contents >>> + return; >>> + } >>> + >>> + // read all raw markup in the body and find the >>> range of the label text inside it. the >>> + // range is specified as the body of the <span >>> class='text'></span> tag. >>> + >>> + AppendingStringBuffer markup = >>> readBodyMarkup(markupStream); >>> + int[] range = findLabelTextRange(markup); >>> + final int start = range[0]; >>> + final int end = range[1]; >>> + >>> + if (start < 0) >>> + { >>> + // if we could not find the range of the >>> label text in the markup we have nothing >>> + // further to do >>> + >>> + super.onComponentTagBody(markupStream, >>> openTag); >>> + return; >>> + } >>> + >>> + // based on whether or not the form component has a >>> label set read or write it into the >>> + // markup >>> + >>> + String label = getFormComponentLabelText(fc); >>> + >>> + if (label != null) >>> + { >>> + // if label is set write it into the markup >>> + >>> + markup = markup.replace(start, end, label); >>> + replaceComponentTagBody(markupStream, >>> openTag, markup); >>> + } >>> + else >>> + { >>> + // if label is not set, read it from the >>> markup into the form component >>> + >>> + String markupLabel = >>> markup.substring(start, end); >>> + fc.setLabel(Model.of(markupLabel)); >>> + super.onComponentTagBody(markupStream, >>> openTag); >>> + } >>> + } >>> + >>> + /** >>> + * Finds start and end index of text in the label. This >>> range is represented by the body of >>> + * the {@code <span class='text'></span>} tag >>> + * >>> + * @param markup >>> + * @return >>> + */ >>> + protected int[] findLabelTextRange(AppendingStringBuffer >>> markup) >>> + { >>> + int[] range = new int[] { -1, -1 }; >>> + >>> + XmlPullParser parser = new XmlPullParser(); >>> + XmlTag opening = null; // opening label text span >>> tag >>> + XmlTag closing = null; // close label text span tag >>> + >>> + try >>> + { >>> + parser.parse(markup); >>> + >>> + XmlTag tag = null; // current tag >>> + >>> + int depth = 0; // depth of span tags >>> + int openDepth = -1; // depth of the label >>> text open span tag >>> + >>> + while (((tag = (XmlTag)parser.nextTag()) != >>> null)) >>> + { >>> + if >>> (!"span".equalsIgnoreCase(tag.getName()) || tag.getNamespace() != null) >>> + { >>> + // skip non-span tags >>> + continue; >>> + } >>> + >>> + if (opening != null && >>> tag.isClose() && depth == openDepth) >>> + { >>> + // found the closing tag we >>> need, we are done >>> + closing = tag; >>> + break; >>> + } >>> + >>> + depth += tag.isOpen() ? 1 : -1; >>> + >>> + if (opening == null && >>> isTextSpan(tag)) >>> + { >>> + // found the opening tag, >>> keep looking for the closing one >>> + opening = tag; >>> + openDepth = depth; >>> + continue; >>> + } >>> + } >>> + } >>> + catch (Exception e) >>> + { >>> + throw new WicketRuntimeException( >>> + "Could not parse markup while >>> processing an auto label for component: " + >>> + fc.toString(false), e); >>> + } >>> + >>> + if (opening != null) >>> + { >>> + // calculate the range of the tag's body, >>> this is where the label text is/will be >>> + range[0] = opening.getPos() + >>> opening.getLength(); >>> + range[1] = closing.getPos(); >>> + } >>> + >>> + return range; >>> + } >>> + >>> + protected AppendingStringBuffer readBodyMarkup(MarkupStream >>> markupStream) >>> + { >>> + int streamIndex = markupStream.getCurrentIndex(); >>> + >>> + AppendingStringBuffer markup = new >>> AppendingStringBuffer(); >>> + do >>> + { >>> + >>> markup.append(((RawMarkup)markupStream.get()).toString()); >>> + markupStream.next(); >>> + } >>> + while ((markupStream.get() instanceof RawMarkup)); >>> + >>> + markupStream.setCurrentIndex(streamIndex); >>> + >>> + return markup; >>> + } >>> + >>> + protected String getFormComponentLabelText(FormComponent<?> >>> fc) >>> + { >>> + String label = fc.getLabel() != null ? >>> fc.getLabel().getObject() : null; >>> + if (label == null) >>> + { >>> + label = >>> fc.getDefaultLabel("wicket:unknown"); >>> + if ("wicket:unknown".equals(label)) >>> + { >>> + label = null; >>> + } >>> + } >>> + return label; >>> + } >>> + >>> + protected final boolean isTextSpan(MarkupElement element) >>> + { >>> + if (!(element instanceof XmlTag)) >>> + return false; >>> + >>> + XmlTag tag = (XmlTag)element; >>> + >>> + if (!tag.isOpen()) >>> + return false; >>> + >>> + if (!"span".equalsIgnoreCase(tag.getName()) || >>> tag.getNamespace() != null) >>> + return false; >>> + >>> + String classNames = >>> tag.getAttributes().getString("class"); >>> + if (Strings.isEmpty(classNames)) >>> + return false; >>> + >>> + boolean textClassFound = false; >>> + for (String className : classNames.split(" ")) >>> + { >>> + if ("text".equals(className)) >>> + { >>> + textClassFound = true; >>> + break; >>> + } >>> + } >>> + if (!textClassFound) >>> + return false; >>> + >>> + >>> + return true; >>> + } >>> + >>> + } >>> + >>> + >>> +} >>> >>> Propchange: >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelResolver.java >>> ------------------------------------------------------------------------------ >>> svn:mime-type = text/plain >>> >>> Added: >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelTagHandler.java >>> URL: >>> http://svn.apache.org/viewvc/wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelTagHandler.java?rev=1144589&view=auto >>> ============================================================================== >>> --- >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelTagHandler.java >>> (added) >>> +++ >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelTagHandler.java >>> Sat Jul 9 04:15:13 2011 >>> @@ -0,0 +1,52 @@ >>> +package org.apache.wicket.markup.html.form; >>> + >>> +import java.text.ParseException; >>> + >>> +import org.apache.wicket.markup.ComponentTag; >>> +import org.apache.wicket.markup.MarkupElement; >>> +import org.apache.wicket.markup.parser.AbstractMarkupFilter; >>> + >>> +/** >>> + * Markup filter that identifies tags with the {@code wicket:for} >>> attribute. See >>> + * {@link AutoLabelResolver} for details. >>> + * >>> + * @author igor >>> + */ >>> +public class AutoLabelTagHandler extends AbstractMarkupFilter >>> +{ >>> + public MarkupElement nextTag() throws ParseException >>> + { >>> + final ComponentTag tag = nextComponentTag(); >>> + if (tag == null || tag.isClose()) >>> + { >>> + return tag; >>> + } >>> + >>> + String related = tag.getAttribute("wicket:for"); >>> + if (related == null) >>> + { >>> + return tag; >>> + } >>> + >>> + related = related.trim(); >>> + if (related.isEmpty()) >>> + { >>> + throw new ParseException("Tag contains an empty >>> wicket:for attribute", tag.getPos()); >>> + } >>> + if (!"label".equalsIgnoreCase(tag.getName())) >>> + { >>> + throw new ParseException("Attribute wicket:for can >>> only be attached to <label> tag", >>> + tag.getPos()); >>> + } >>> + if (tag.getId() != null) >>> + { >>> + throw new ParseException( >>> + "Attribute wicket:for cannot be used in >>> conjunction with wicket:id", tag.getPos()); >>> + } >>> + >>> + tag.setId(getClass().getName()); >>> + tag.setModified(true); >>> + return tag; >>> + } >>> + >>> +} >>> >>> Propchange: >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/markup/html/form/AutoLabelTagHandler.java >>> ------------------------------------------------------------------------------ >>> svn:mime-type = text/plain >>> >>> Modified: >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/protocol/http/WebApplication.java >>> URL: >>> http://svn.apache.org/viewvc/wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/protocol/http/WebApplication.java?rev=1144589&r1=1144588&r2=1144589&view=diff >>> ============================================================================== >>> --- >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/protocol/http/WebApplication.java >>> (original) >>> +++ >>> wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/protocol/http/WebApplication.java >>> Sat Jul 9 04:15:13 2011 >>> @@ -34,6 +34,7 @@ import org.apache.wicket.Response; >>> import org.apache.wicket.Session; >>> import org.apache.wicket.WicketRuntimeException; >>> import org.apache.wicket.ajax.AjaxRequestTarget; >>> +import org.apache.wicket.markup.html.form.AutoLabelResolver; >>> import org.apache.wicket.markup.html.pages.AccessDeniedPage; >>> import org.apache.wicket.markup.html.pages.InternalErrorPage; >>> import org.apache.wicket.markup.html.pages.PageExpiredErrorPage; >>> @@ -555,6 +556,7 @@ public abstract class WebApplication ext >>> >>> // Add resolver for automatically resolving HTML links >>> getPageSettings().addComponentResolver(new >>> AutoLinkResolver()); >>> + getPageSettings().addComponentResolver(new >>> AutoLabelResolver()); >>> >>> // Set resource finder to web app path >>> getResourceSettings().setResourceFinder(getResourceFinder()); >>> >>> Added: >>> wicket/branches/wicket-1.4.x/wicket/src/test/java/org/apache/wicket/markup/html/form/AutoLabelTest.java >>> URL: >>> http://svn.apache.org/viewvc/wicket/branches/wicket-1.4.x/wicket/src/test/java/org/apache/wicket/markup/html/form/AutoLabelTest.java?rev=1144589&view=auto >>> ============================================================================== >>> --- >>> wicket/branches/wicket-1.4.x/wicket/src/test/java/org/apache/wicket/markup/html/form/AutoLabelTest.java >>> (added) >>> +++ >>> wicket/branches/wicket-1.4.x/wicket/src/test/java/org/apache/wicket/markup/html/form/AutoLabelTest.java >>> Sat Jul 9 04:15:13 2011 >>> @@ -0,0 +1,198 @@ >>> +/* >>> + * 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.html.form; >>> + >>> +import org.apache.wicket.MarkupContainer; >>> +import org.apache.wicket.Page; >>> +import org.apache.wicket.WicketTestCase; >>> +import org.apache.wicket.markup.IMarkupCacheKeyProvider; >>> +import org.apache.wicket.markup.IMarkupResourceStreamProvider; >>> +import org.apache.wicket.markup.html.WebPage; >>> +import org.apache.wicket.model.Model; >>> +import org.apache.wicket.util.resource.IResourceStream; >>> +import org.apache.wicket.util.resource.StringResourceStream; >>> + >>> +/** >>> + * Tests {@code wicket:for} attribute functionality >>> + * >>> + * @author igor >>> + */ >>> +public class AutoLabelTest extends WicketTestCase >>> +{ >>> + public void testLabelIntoMarkupInsertion() >>> + { >>> + class MyTestPage extends TestPage >>> + { >>> + public MyTestPage(String labelMarkup) >>> + { >>> + super("<label wicket:for='t'>" + >>> labelMarkup + "</label>"); >>> + field.setLabel(Model.of("t")); >>> + } >>> + } >>> + >>> + // simple insertion >>> + assertRendered(new MyTestPage("<span >>> class='text'>text</span>"), >>> + "<span class='text'>t</span>"); >>> + >>> + >>> + // preserves markup before and after >>> + assertRendered(new MyTestPage(" <div> a </div> <span >>> class='text'>text</span> b "), >>> + " <div> a </div> <span class='text'>t</span> b "); >>> + >>> + >>> + // embedded span tags >>> + assertRendered(new MyTestPage(" a <div> b <span >>> class='text'>text</span> c </div> d"), >>> + " a <div> b <span class='text'>t</span> c </div> >>> d"); >>> + >>> + // double text span tags - only the first one is touched >>> + assertRendered(new MyTestPage( >>> + "<span class='text'>text</span><span >>> class='text'>text</span>"), >>> + "<span class='text'>t</span><span >>> class='text'>text</span>"); >>> + >>> + // no span - no insertion >>> + assertRendered(new MyTestPage(" text "), " text "); >>> + >>> + // empty label tag >>> + assertRendered(new MyTestPage(""), "></label>"); >>> + >>> + // empty span tag >>> + assertRendered(new MyTestPage("<span >>> class='text'></span>"), "<span class='text'>t</span>"); >>> + >>> + // open/close span tag >>> + assertRendered(new MyTestPage("<span class='text'/>"), >>> "<span class='text'>t</span>"); >>> + >>> + // test additional classes on the span are preserved >>> + assertRendered(new MyTestPage("<span class='foo text >>> bar'/>"), >>> + "<span class='foo text bar'>t</span>"); >>> + } >>> + >>> + public void testMarkupIntoLabelInsertion() >>> + { >>> + class MyTestPage extends TestPage >>> + { >>> + public MyTestPage(String labelMarkup) >>> + { >>> + super("<label wicket:for='t'>" + >>> labelMarkup + "</label>"); >>> + } >>> + } >>> + >>> + // test form component label is defaulted to the contents >>> of span class='text' >>> + >>> + MyTestPage page = new MyTestPage("<span >>> class='text'>text</span>"); >>> + tester.startPage(page); >>> + assertEquals("text", >>> ((MyTestPage)tester.getLastRenderedPage()).field.getLabel() >>> + .getObject()); >>> + } >>> + >>> + public void testLabelTagClasses() >>> + { >>> + class MyTestPage extends TestPage >>> + { >>> + public MyTestPage() >>> + { >>> + super("<label wicket:for='t'><span >>> class='text'>field</span></label>"); >>> + } >>> + } >>> + >>> + // test required class >>> + MyTestPage page = new MyTestPage(); >>> + assertNotRendered(page, "class='required'"); >>> + page.field.setRequired(true); >>> + assertRendered(page, "class='required'"); >>> + >>> + // test error class >>> + page = new MyTestPage(); >>> + assertNotRendered(page, "class='error'"); >>> + page.field.error("too short"); >>> + assertRendered(page, "class='error'"); >>> + >>> + // test classes are appended and not overridden >>> + page = new MyTestPage(); >>> + page.field.setRequired(true); >>> + page.field.error("too short"); >>> + tester.startPage(page); >>> + String markup = tester.getServletResponse().getDocument(); >>> + assertTrue(markup.contains("class=\"required error\"") || >>> + markup.contains("class=\"error required\"")); >>> + >>> + // test existing classes are preserved >>> + class MyTestPage2 extends TestPage >>> + { >>> + public MyTestPage2() >>> + { >>> + super("<label class='long' >>> wicket:for='t'><span class='text'>field</span></label>"); >>> + } >>> + } >>> + >>> + MyTestPage2 page2 = new MyTestPage2(); >>> + page2.field.setRequired(true); >>> + tester.startPage(page2); >>> + markup = tester.getServletResponse().getDocument(); >>> + assertTrue(markup.contains("class=\"required long\"") || >>> + markup.contains("class=\"long required\"")); >>> + >>> + } >>> + >>> + private void assertRendered(Page page, String markupFragment) >>> + { >>> + tester.startPage(page); >>> + String markup = tester.getServletResponse().getDocument(); >>> + markup = markup.replace("'", "\""); >>> + assertTrue("fragment: [" + markupFragment + "] not found in >>> generated markup: [" + markup + >>> + "]", markup.contains(markupFragment.replace("'", >>> "\""))); >>> + } >>> + >>> + private void assertNotRendered(Page page, String markupFragment) >>> + { >>> + tester.startPage(page); >>> + String markup = tester.getServletResponse().getDocument(); >>> + markup = markup.replace("'", "\""); >>> + assertFalse("fragment: [" + markupFragment + "] not found >>> in generated markup: [" + markup + >>> + "]", markup.contains(markupFragment.replace("'", >>> "\""))); >>> + } >>> + >>> + private static class TestPage extends WebPage >>> + implements >>> + IMarkupResourceStreamProvider, >>> + IMarkupCacheKeyProvider >>> + { >>> + TextField<String> field; >>> + >>> + private final String labelMarkup; >>> + >>> + public TestPage(String labelMarkup) >>> + { >>> + this.labelMarkup = labelMarkup; >>> + Form<?> form = new Form<Void>("f"); >>> + add(form); >>> + form.add(field = new TextField<String>("t", >>> Model.of(""))); >>> + } >>> + >>> + public IResourceStream >>> getMarkupResourceStream(MarkupContainer container, >>> + Class<?> containerClass) >>> + { >>> + return new StringResourceStream("<html><body><form >>> wicket:id='f'>\n" + labelMarkup + >>> + "\n<input type='text' >>> wicket:id='t'/>\n</form></body></html>"); >>> + } >>> + >>> + public String getCacheKey(MarkupContainer container, >>> Class<?> containerClass) >>> + { >>> + // no cache >>> + return null; >>> + } >>> + } >>> +} >>> >>> Propchange: >>> wicket/branches/wicket-1.4.x/wicket/src/test/java/org/apache/wicket/markup/html/form/AutoLabelTest.java >>> ------------------------------------------------------------------------------ >>> svn:mime-type = text/plain >>> >>> >>> >> >> >> >> -- >> Martin Grigorov >> jWeekend >> Training, Consulting, Development >> http://jWeekend.com >> >
-- Martin Grigorov jWeekend Training, Consulting, Development http://jWeekend.com
