/* * 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.cocoon.forms.formmodel; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import org.apache.cocoon.forms.FormsException; import org.apache.cocoon.forms.FormsRuntimeException; import org.apache.cocoon.util.location.Location; // TODO: Refine and i18n the exception messages. /** * Helper class for the Definition implementation of widgets containing * other widgets. * * @version $Id$ */ public class WidgetDefinitionList { private List widgetDefinitions = new ArrayList(); private Map widgetDefinitionsById = new HashMap(); private WidgetDefinition containerDefinition; private boolean wasHere; private ListIterator definitionsIt = widgetDefinitions.listIterator(); /** * @param definition the widget definition to which this container delegate belongs */ public WidgetDefinitionList(WidgetDefinition definition) { this.containerDefinition = definition; wasHere = false; } public int size() { return widgetDefinitions.size(); } public void addWidgetDefinition(WidgetDefinition widgetDefinition) throws DuplicateIdException { String id = widgetDefinition.getId(); // Do not add NewDefinition id's hash. if (!(widgetDefinition instanceof NewDefinition)) { if (widgetDefinitionsById.containsKey(id)) { Location containerLocation = containerDefinition.getLocation(); Location firstLocation = getWidgetDefinition(id).getLocation(); throw new DuplicateIdException( "Detected duplicate widget id '" + id + "'.\n" + "Container widget '" + containerDefinition.getId() + "' at " + containerLocation + "\n" + "already contains a widget with the same id at " + firstLocation + ".", widgetDefinition); } widgetDefinitionsById.put(widgetDefinition.getId(), widgetDefinition); } this.definitionsIt.add(widgetDefinition); } public List getWidgetDefinitions() { return widgetDefinitions; } public boolean hasWidget(String id) { return widgetDefinitionsById.containsKey(id); } public WidgetDefinition getWidgetDefinition(String id) { return (WidgetDefinition) widgetDefinitionsById.get(id); } public void resolve(List parents, WidgetDefinition parent) throws Exception { if (!wasHere) { wasHere = true; this.definitionsIt = widgetDefinitions.listIterator(); parents.add(containerDefinition); while (this.definitionsIt.hasNext()) { WidgetDefinition widgetDefinition = (WidgetDefinition) this.definitionsIt.next(); // ClassDefinition's get resolved by NewDefinition rather than here. if (!(widgetDefinition instanceof ClassDefinition)) { if (widgetDefinition instanceof NewDefinition) { // Remove NewDefinition in preparation for its referenced class of widget definitions to be added. this.definitionsIt.remove(); ((NewDefinition) widgetDefinition).resolve(parents, containerDefinition); } else { if (widgetDefinition instanceof ContainerDefinition) { ((ContainerDefinition) widgetDefinition).resolve(parents, containerDefinition); } } } } parents.remove(parents.size()-1); wasHere = false; } else { // Non-terminating recursion detection // Search up parent list in hopes of finding a "Union" or "Repeater" before finding previous "New" for this "Class". ListIterator parentsIt = parents.listIterator(parents.size()); while(parentsIt.hasPrevious()) { WidgetDefinition widgetDefinition = (WidgetDefinition)parentsIt.previous(); if (widgetDefinition instanceof UnionDefinition) { break; } if (widgetDefinition instanceof RepeaterDefinition) { break; } if (widgetDefinition == containerDefinition) { Location location = containerDefinition.getLocation(); if (parent instanceof FormDefinition) { throw new FormsException("Container: Non-terminating recursion detected in form definition.", location); } throw new FormsException("Container: Non-terminating recursion detected in widget definition: " + parent.getId(), location); } } } } public void createWidget(Widget parent, String id) { WidgetDefinition widgetDefinition = (WidgetDefinition) widgetDefinitionsById.get(id); if (widgetDefinition == null) { throw new FormsRuntimeException(containerDefinition.getId() + ": WidgetDefinition '" + id + "' does not exist.", containerDefinition.getLocation()); } Widget widget = widgetDefinition.createInstance(); if (widget != null) { ((ContainerWidget) parent).addChild(widget); } } public void createWidgets(Widget parent) { Iterator i = widgetDefinitions.iterator(); while (i.hasNext()) { WidgetDefinition widgetDefinition = (WidgetDefinition) i.next(); Widget widget = widgetDefinition.createInstance(); if (widget != null) { ((ContainerWidget) parent).addChild(widget); } } } public void checkCompleteness() throws IncompletenessException { if (!wasHere) { wasHere = true; // FIXME: is it legal to have no widgets in a container? There are some cases of this in Swan // if(size() == 0) // throw new IncompletenessException(this.containerDefinition.getClass().getName() + // " requires at least one child widget!", this.containerDefinition); // now check children's completeness Iterator i = widgetDefinitions.iterator(); while (i.hasNext()) { ((WidgetDefinition) i.next()).checkCompleteness(); } wasHere = false; } } }