001// Copyright 2007, 2008, 2009, 2010 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007// http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry5.internal;
016
017import org.apache.tapestry5.*;
018import org.apache.tapestry5.dom.Element;
019import org.apache.tapestry5.services.Environment;
020import org.apache.tapestry5.services.FormSupport;
021
022/**
023 * Default implementation that writes an attribute into fields or labels that are in error.
024 */
025public final class DefaultValidationDecorator extends BaseValidationDecorator
026{
027    private final Environment environment;
028
029    private final Asset spacerAsset;
030
031    private final MarkupWriter markupWriter;
032
033    /**
034     * @param environment
035     *            used to locate objects and services during the render
036     * @param spacerAsset
037     *            asset for a one-pixel spacer image used as a placeholder for the error marker icon
038     * @param markupWriter
039     */
040    public DefaultValidationDecorator(Environment environment, Asset spacerAsset, MarkupWriter markupWriter)
041    {
042        this.environment = environment;
043        this.spacerAsset = spacerAsset;
044        this.markupWriter = markupWriter;
045    }
046
047    @Override
048    public void insideField(Field field)
049    {
050        if (inError(field))
051            addErrorClassToCurrentElement();
052    }
053
054    @Override
055    public void insideLabel(Field field, Element element)
056    {
057        if (field == null)
058            return;
059
060        if (inError(field))
061            element.addClassName(CSSClassConstants.ERROR);
062    }
063
064    /**
065     * Writes an icon for field after the field. The icon has the same id as the field, with ":icon" appended. This is
066     * expected by the default client-side JavaScript. The icon's src is a blank spacer image (this is to allow the
067     * image displayed to be overridden via CSS). The icon's CSS class is "t-error-icon", with "t-invisible" added
068     * if the field is not in error when rendered. If client validation is not enabled for the form containing the
069     * field and the field is not in error, then the error icon itself is not rendered.
070     * 
071     * @param field
072     *            which just completed rendering itself
073     */
074    @Override
075    public void afterField(Field field)
076    {
077        boolean inError = inError(field);
078
079        boolean clientValidationEnabled = getFormSupport().isClientValidationEnabled();
080
081        if (inError || clientValidationEnabled)
082        {
083            String iconId = field.getClientId() + "_icon";
084
085            String cssClass = inError ? "t-error-icon" : "t-error-icon t-invisible";
086
087            markupWriter.element("img", "src", spacerAsset.toClientURL(), "alt", "", "class", cssClass, "id", iconId);
088            markupWriter.end();
089        }
090
091    }
092
093    private FormSupport getFormSupport()
094    {
095        return environment.peekRequired(FormSupport.class);
096    }
097
098    private boolean inError(Field field)
099    {
100        ValidationTracker tracker = environment.peekRequired(ValidationTracker.class);
101
102        return tracker.inError(field);
103    }
104
105    private void addErrorClassToCurrentElement()
106    {
107        markupWriter.getElement().addClassName(CSSClassConstants.ERROR);
108    }
109}