/*
* ============================================================================
* GNU Lesser General Public License
* ============================================================================
*
* Beanlet - JSE Application Container.
* Copyright (C) 2006 Leon van Zantvoort
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Leon van Zantvoort
* 243 Acalanes Drive #11
* Sunnyvale, CA 94086
* USA
*
* zantvoort@users.sourceforge.net
* http://beanlet.org
*/
package org.beanlet.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import org.beanlet.BeanletDefinitionException;
import org.beanlet.plugin.ClassResolver;
import org.beanlet.plugin.ElementAnnotationFactory;
import org.beanlet.plugin.NestedBeanletFactory;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
*
* @author Leon van Zantvoort
*/
final class NestedBeanletFactoryImpl implements NestedBeanletFactory {
private final String outerBeanletName;
private final ClassLoader loader;
private final ElementAnnotationFactory annotationFactory;
private final ClassResolver resolver;
private final Map<String, XMLAnnotationDomain<?>> map;
private final Map<String, Node> nodes;
private final AtomicBoolean inContext;
public NestedBeanletFactoryImpl(String outerBeanletName,
ClassLoader loader,
ElementAnnotationFactory annotationFactory,
ClassResolver resolver,
Map<String, XMLAnnotationDomain<?>> map) {
this.outerBeanletName = outerBeanletName;
this.loader = loader;
this.annotationFactory = annotationFactory;
this.resolver = resolver;
this.map = map;
this.nodes = new LinkedHashMap<String, Node>();
this.inContext = new AtomicBoolean(true);
}
/**
* The name of the nested beanlet is simply the outer beanlet name with
* a postfix. This postfix is the "$" character plus the name of the
* nested beanlet. If no name is specified, this name is replaced with
* a number, making the entire name unique. Contrary to top level
* beanlets, the beanlet's type is not used to construct the postfix, as
* this type might not be known at this moment.
*/
public String create(Node beanletNode) {
if (!inContext.get()) {
throw new IllegalStateException(
"Nested beanlets can only be created while in scope of " +
"the ElementAnnotationFactory methods.");
}
NamedNodeMap attributes = beanletNode.getAttributes();
Node nameNode = attributes.getNamedItem("name");
String nestedBeanletName = nameNode == null ? null : nameNode.getNodeValue();
if (nestedBeanletName == null) {
nestedBeanletName = XMLAnnotationDomain.
getAnonymousBeanletName(outerBeanletName + "$");
} else {
if (nestedBeanletName.contains("$")) {
throw new BeanletDefinitionException(nestedBeanletName,
"$ is a reserved character.");
}
nestedBeanletName = outerBeanletName + "$" + nestedBeanletName;
}
if (nodes.put(nestedBeanletName, beanletNode) != null) {
throw new BeanletDefinitionException(nestedBeanletName,
"Duplicate declaration of beanlet.");
}
return nestedBeanletName;
}
public List<XMLAnnotationDomain<?>> get() {
inContext.set(false);
List<XMLAnnotationDomain<?>> list =
new ArrayList<XMLAnnotationDomain<?>>();
for (Map.Entry<String, Node> entry : nodes.entrySet()) {
String beanletName = entry.getKey();
Node beanletNode = entry.getValue();
XMLAnnotationDomain<?> parentDomain = null;
NamedNodeMap attributes = beanletNode.getAttributes();
Node parentNode = attributes.getNamedItem("parent");
if (parentNode != null) {
String parent = parentNode.getNodeValue();
parentDomain = map.get(parent);
if (parentDomain == null) {
throw new BeanletDefinitionException(beanletName,
"Parent does not exist: '" + parent + "'.");
}
}
// Add domain to map, so that it is available at nestedFactory.get.
Map<String, XMLAnnotationDomain<?>> parentDomains =
new HashMap<String, XMLAnnotationDomain<?>>(map);
NestedBeanletFactoryImpl nestedFactory =
new NestedBeanletFactoryImpl(beanletName, loader,
annotationFactory, resolver, parentDomains);
XMLAnnotationDomain<?> domain = XMLAnnotationDomain.createNestedDomain(beanletName,
beanletNode, loader, annotationFactory, resolver,
parentDomain, nestedFactory);
map.put(beanletName, domain);
parentDomains.put(beanletName, domain);
String nestedBeanletName = beanletName.substring(
outerBeanletName.length() + 1);
if (!nestedBeanletName.startsWith("$")) {
map.put(nestedBeanletName, domain);
parentDomains.put(nestedBeanletName, domain);
}
list.add(domain);
list.addAll(nestedFactory.get());
}
return list;
}
}