001// Copyright 2012 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.corelib.base;
016
017import org.apache.tapestry5.ClientElement;
018import org.apache.tapestry5.ComponentResources;
019import org.apache.tapestry5.annotations.Environmental;
020import org.apache.tapestry5.dom.Element;
021import org.apache.tapestry5.ioc.annotations.Inject;
022import org.apache.tapestry5.services.javascript.JavaScriptSupport;
023
024/**
025 * Provides support for elements that will optionally render a unique {@code id} attribute, but only if it is
026 * requested. Subclasses should invoke {@link #storeElement(org.apache.tapestry5.dom.Element)}
027 * when they begin an element that requires an id.
028 *
029 * @since 5.4
030 */
031public abstract class BaseClientElement implements ClientElement
032{
033    private Element element;
034
035    private String clientId;
036
037    @Inject
038    protected ComponentResources resources;
039
040    @Environmental
041    protected JavaScriptSupport javaScriptSupport;
042
043    /**
044     * Invoked (usually from a {@link org.apache.tapestry5.annotations.BeginRender} phase method) to assign
045     * the element, and clear the clientId (only relevant for components that render in a loop).
046     * @param element the element to store
047     */
048    protected void storeElement(Element element)
049    {
050        assert element != null;
051
052        this.element = element;
053        clientId = null; // Until asked.
054    }
055
056    /**
057     * When invoked the first time (per request), a unique id is assigned and and id attribute added to the {@linkplain #element element} for
058     * the component.
059     */
060    public String getClientId()
061    {
062
063        if (clientId == null)
064        {
065            if (element == null)
066            {
067                throw new IllegalStateException(String.format("Component %s has not yet rendered; it is not possible to request its client id until after it has begun rendering.",
068                        resources.getCompleteId()));
069            }
070
071            clientId = javaScriptSupport.allocateClientId(resources);
072
073            element.forceAttributes("id", clientId);
074        }
075
076        return clientId;
077    }
078}