blob: 45d9d7d670cb179e553fe1528f08ac4c4cba728e [file] [log] [blame]
/*
* 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.servicemix.kernel.gshell.core.config;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Element;
import org.apache.geronimo.gshell.command.Alias;
import org.apache.geronimo.gshell.command.Link;
import org.apache.geronimo.gshell.wisdom.command.AliasImpl;
import org.apache.geronimo.gshell.wisdom.command.ConfigurableCommandCompleter;
import org.apache.geronimo.gshell.wisdom.command.LinkImpl;
import org.apache.geronimo.gshell.wisdom.registry.CommandLocationImpl;
import org.apache.servicemix.kernel.gshell.core.CommandBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
public class CommandParser extends AbstractBeanDefinitionParser {
public static final String ID = ID_ATTRIBUTE;
public static final String DESCRIPTION = "description";
public static final String PLUGIN_TEMPLATE = "pluginTemplate";
public static final String ACTION = "action";
public static final String ACTION_ID = "actionId";
public static final String COMMAND_TEMPLATE_SUFFIX = "CommandTemplate";
public static final String COMMAND_BUNDLE = "command-bundle";
public static final String NAME = "name";
public static final String LOCATION = "location";
public static final String COMMANDS = "commands";
public static final String COMMAND = "command";
public static final String TYPE = "type";
public static final String DOCUMENTER = "documenter";
public static final String COMPLETER = "completer";
public static final String COMPLETERS = "completers";
public static final String BEAN = "bean";
public static final String REF = "ref";
public static final String NULL = "null";
public static final String MESSAGE_SOURCE = "message-source";
public static final String MESSAGES = "messages";
public static final String PROTOTYPE = "prototype";
public static final String ALIAS = "alias";
public static final String ALIASES = "aliases";
public static final String LINK = "link";
public static final String LINKS = "links";
public static final String TARGET = "target";
@Override
protected boolean shouldGenerateId() {
return true;
}
@Override
protected boolean shouldGenerateIdAsFallback() {
return true;
}
protected AbstractBeanDefinition parseInternal(final Element element, final ParserContext context) {
assert element != null;
assert context != null;
Builder builder = new Builder(context);
BeanDefinitionBuilder plugin = builder.parseCommandBundle(element);
return plugin.getBeanDefinition();
}
/**
* Helper to deal with command type.
*/
private enum CommandType
{
STATELESS,
STATEFUL;
public static CommandType parse(final String text) {
assert text != null;
return valueOf(text.toUpperCase());
}
public String getTemplateName() {
return name().toLowerCase() + COMMAND_TEMPLATE_SUFFIX;
}
public void wire(final BeanDefinitionBuilder command, final BeanDefinitionHolder action) {
assert command != null;
assert action != null;
switch (this) {
case STATELESS:
command.addPropertyReference(ACTION, action.getBeanName());
break;
case STATEFUL:
command.addPropertyValue(ACTION_ID, action.getBeanName());
break;
}
}
}
/**
* Helper to build plugin related bean definitions.
*/
private class Builder
{
private final Logger log = LoggerFactory.getLogger(getClass());
private ParserContext context;
public Builder(final ParserContext context) {
assert context != null;
this.context = context;
}
private String resolveId(final Element element, final BeanDefinition def) throws BeanDefinitionStoreException {
assert element != null;
assert def != null;
if (shouldGenerateId()) {
return context.getReaderContext().generateBeanName(def);
}
String id = element.getAttribute(ID_ATTRIBUTE);
if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback()) {
id = context.getReaderContext().generateBeanName(def);
}
return id;
}
@SuppressWarnings({"unchecked"})
private List<Element> getChildElements(final Element element, final String name) {
assert element != null;
assert name != null;
return DomUtils.getChildElementsByTagName(element, name);
}
@SuppressWarnings({"unchecked"})
private List<Element> getChildElements(final Element element, final String[] names) {
assert element != null;
assert names != null;
return DomUtils.getChildElementsByTagName(element, names);
}
@SuppressWarnings({"unchecked"})
private Element getChildElement(final Element element, final String name) {
assert element != null;
assert name != null;
List<Element> elements = DomUtils.getChildElementsByTagName(element, name);
if (elements != null && !elements.isEmpty()) {
return elements.get(0);
}
return null;
}
private BeanDefinitionParserDelegate createBeanDefinitionParserDelegate(final Element element) {
assert element != null;
BeanDefinitionParserDelegate parser = new BeanDefinitionParserDelegate(context.getReaderContext());
parser.initDefaults(element.getOwnerDocument().getDocumentElement());
return parser;
}
private BeanDefinitionHolder parseBeanDefinitionElement(final Element element) {
assert element != null;
BeanDefinitionParserDelegate parser = createBeanDefinitionParserDelegate(element);
return parser.parseBeanDefinitionElement(element);
}
private void parseAndApplyDescription(final Element element, final BeanDefinition def) {
assert element != null;
assert def != null;
Element desc = getChildElement(element, DESCRIPTION);
if (desc != null) {
if (def instanceof AbstractBeanDefinition) {
((AbstractBeanDefinition)def).setDescription(desc.getTextContent());
}
}
}
private void parseAndApplyDescription(final Element element, final BeanDefinitionBuilder builder) {
assert element != null;
assert builder != null;
parseAndApplyDescription(element, builder.getRawBeanDefinition());
}
private BeanDefinitionHolder register(final BeanDefinitionHolder holder) {
assert holder != null;
registerBeanDefinition(holder, context.getRegistry());
return holder;
}
private BeanDefinitionHolder register(final BeanDefinition def, final String id) {
assert def != null;
assert id != null;
BeanDefinitionHolder holder = new BeanDefinitionHolder(def, id);
return register(holder);
}
//
// <gshell:command-bundle>
//
private BeanDefinitionBuilder parseCommandBundle(final Element element) {
assert element != null;
log.trace("Parse command bundle; element; {}", element);
BeanDefinitionBuilder bundle = BeanDefinitionBuilder.rootBeanDefinition(CommandBundle.class);
parseAndApplyDescription(element, bundle);
//
// TODO: Figure out how we can save the order of <gshell:command> and <gshell:link> so that 'help' displays them in the order they are defined
//
ManagedList commands = new ManagedList();
commands.addAll(parseCommands(element));
bundle.addPropertyValue(COMMANDS, commands);
ManagedList links = new ManagedList();
links.addAll(parseLinks(element));
bundle.addPropertyValue(LINKS, links);
ManagedList aliases = new ManagedList();
aliases.addAll(parseAliases(element));
bundle.addPropertyValue(ALIASES, aliases);
return bundle;
}
//
// <gshell:command>
//
private List<BeanDefinition> parseCommands(final Element element) {
assert element != null;
log.trace("Parse commands; element; {}", element);
List<BeanDefinition> commands = new ArrayList<BeanDefinition>();
List<Element> children = getChildElements(element, COMMAND);
for (Element child : children) {
BeanDefinitionBuilder command = parseCommand(child);
commands.add(command.getBeanDefinition());
}
return commands;
}
private BeanDefinitionBuilder parseCommand(final Element element) {
assert element != null;
log.trace("Parse command; element; {}", element);
CommandType type = CommandType.parse(element.getAttribute(TYPE));
BeanDefinitionBuilder command = BeanDefinitionBuilder.childBeanDefinition(type.getTemplateName());
parseAndApplyDescription(element, command);
Element child;
// Required children elements
String name = element.getAttribute(NAME);
BeanDefinition def = new GenericBeanDefinition();
def.setBeanClassName(CommandLocationImpl.class.getName());
def.getConstructorArgumentValues().addGenericArgumentValue(name);
command.addPropertyValue(LOCATION, def);
child = getChildElement(element, ACTION);
BeanDefinitionHolder action = parseCommandAction(child);
type.wire(command, action);
// Optional children elements
child = getChildElement(element, DOCUMENTER);
if (child != null) {
BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
command.addPropertyValue(DOCUMENTER, holder.getBeanDefinition());
}
child = getChildElement(element, COMPLETER);
if (child != null) {
BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
command.addPropertyValue(COMPLETER, holder.getBeanDefinition());
}
child = getChildElement(element, COMPLETERS);
if (child != null) {
BeanDefinitionBuilder completer = parseCommandCompleters(child);
command.addPropertyValue(COMPLETER, completer.getBeanDefinition());
}
child = getChildElement(element, MESSAGE_SOURCE);
if (child != null) {
BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
command.addPropertyValue(MESSAGES, holder.getBeanDefinition());
}
//String id = resolveId(element, command.getBeanDefinition());
//BeanDefinitionHolder holder = register(command.getBeanDefinition(), id);
return command;
}
//
// <gshell:completers>
//
private BeanDefinitionBuilder parseCommandCompleters(final Element element) {
assert element != null;
BeanDefinitionBuilder completer = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableCommandCompleter.class);
ManagedList completers = new ManagedList();
List<Element> children = getChildElements(element, new String[] {BEAN, REF, NULL});
for (Element child : children) {
if (DomUtils.nodeNameEquals(child, BEAN)) {
BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
// noinspection unchecked
completers.add(holder.getBeanDefinition());
}
else if (DomUtils.nodeNameEquals(child, REF)) {
BeanDefinitionParserDelegate parser = createBeanDefinitionParserDelegate(child);
RuntimeBeanReference ref = (RuntimeBeanReference) parser.parsePropertySubElement(child, completer.getRawBeanDefinition());
// noinspection unchecked
completers.add(ref);
}
else if (DomUtils.nodeNameEquals(child, NULL)) {
// noinspection unchecked
completers.add(null);
}
}
completer.addConstructorArgValue(completers);
return completer;
}
//
// <gshell:action>
//
private BeanDefinitionHolder parseCommandAction(final Element element) {
assert element != null;
log.trace("Parse command action; element; {}", element);
// Construct the action
BeanDefinition action = parseBeanDefinitionElement(element).getBeanDefinition();
// All actions are configured as prototypes
action.setScope(PROTOTYPE);
// Generate id and register the bean
String id = resolveId(element, action);
return register(action, id);
}
//
// <gshell:link>
//
private List<Link> parseLinks(final Element element) {
assert element != null;
log.trace("Parse links; element; {}", element);
List<Link> links = new ArrayList<Link>();
List<Element> children = getChildElements(element, LINK);
for (Element child : children) {
String name = child.getAttribute(NAME);
String target = child.getAttribute(TARGET);
links.add(new LinkImpl(name, target));
}
return links;
}
//
// <gshell:alias>
//
private List<Alias> parseAliases(final Element element) {
assert element != null;
log.trace("Parse aliases; element; {}", element);
List<Alias> aliases = new ArrayList<Alias>();
List<Element> children = getChildElements(element, ALIAS);
for (Element child : children) {
String name = child.getAttribute(NAME);
String alias = child.getAttribute(ALIAS);
aliases.add(new AliasImpl(name, alias));
}
return aliases;
}
}
}