/*
* Copyright 2013 the original author or authors.
*
* Licensed 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.springframework.xd.dirt.stream;
import static org.springframework.xd.dirt.stream.dsl.XDDSLMessages.NAMED_CHANNELS_UNSUPPORTED_HERE;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.springframework.data.repository.CrudRepository;
import org.springframework.util.Assert;
import org.springframework.validation.BindException;
import org.springframework.xd.dirt.core.BaseDefinition;
//import org.springframework.xd.dirt.module.ModuleDefinitionRepository;
//import org.springframework.xd.dirt.module.NoSuchModuleException;
//import org.springframework.xd.dirt.plugins.ModuleConfigurationException;
//import org.springframework.xd.dirt.stream.ParsingContext.Position;
import org.springframework.xd.dirt.stream.dsl.ArgumentNode;
import org.springframework.xd.dirt.stream.dsl.ModuleNode;
import org.springframework.xd.dirt.stream.dsl.SinkChannelNode;
import org.springframework.xd.dirt.stream.dsl.SourceChannelNode;
import org.springframework.xd.dirt.stream.dsl.StreamConfigParser;
import org.springframework.xd.dirt.stream.dsl.StreamDefinitionException;
import org.springframework.xd.dirt.stream.dsl.StreamNode;
//import org.springframework.xd.module.ModuleDefinition;
//import org.springframework.xd.module.ModuleDescriptor;
//import org.springframework.xd.module.ModuleType;
//import org.springframework.xd.module.core.CompositeModule;
//import org.springframework.xd.module.options.ModuleOptionsMetadata;
//import org.springframework.xd.module.options.ModuleOptionsMetadataResolver;
/**
* Parser to convert a DSL string for a stream into a list of
* {@link org.springframework.xd.module.ModuleDescriptor}
* objects that comprise the given stream.
*
* @author Andy Clement
* @author Gunnar Hillert
* @author Glenn Renfro
* @author Mark Fisher
* @author Patrick Peralta
* @since 1.0
*/
public class XDStreamParser implements XDParser {
/**
* Optional definition repository used to obtain sub-stream/label
* references.
*
* @see org.springframework.xd.dirt.stream.dsl.StreamConfigParser
*/
private CrudRepository<? extends BaseDefinition, String> repository;
/**
* Repository for user defined modules.
*/
// private final ModuleDefinitionRepository moduleDefinitionRepository;
/**
* Resolver for module options metadata.
*/
// private final ModuleOptionsMetadataResolver moduleOptionsMetadataResolver;
/**
* Construct an {@code XDStreamParser}.
*
* @param repository repository for stream definitions (optional)
* @param moduleDefinitionRepository repository for user defined modules
* @param moduleOptionsMetadataResolver resolver for module options metadata
*/
public XDStreamParser(CrudRepository<? extends BaseDefinition, String> repository
// ,
// ModuleDefinitionRepository moduleDefinitionRepository,
// ModuleOptionsMetadataResolver moduleOptionsMetadataResolver
) {
// Assert.notNull(moduleDefinitionRepository, "moduleDefinitionRepository can not be null");
// Assert.notNull(moduleOptionsMetadataResolver, "moduleOptionsMetadataResolver can not be null");
this.repository = repository;
// this.moduleDefinitionRepository = moduleDefinitionRepository;
// this.moduleOptionsMetadataResolver = moduleOptionsMetadataResolver;
}
// @Override
// public List<ModuleDescriptor> parse(String name, String config,
// ParsingContext type) {
// // TODO Auto-generated method stub
// return null;
// }
// @Override
// public List<ModuleDescriptor> parse(String name, String config,
// ParsingContext type) {
// // TODO Auto-generated method stub
// return null;
// }
/**
* Construct an {@code XDStreamParser}.
*
* @param moduleDefinitionRepository repository for user defined modules
* @param moduleOptionsMetadataResolver resolver for module options metadata
*/
// public XDStreamParser(ModuleDefinitionRepository moduleDefinitionRepository,
// ModuleOptionsMetadataResolver moduleOptionsMetadataResolver) {
// this(null, moduleDefinitionRepository, moduleOptionsMetadataResolver);
// }
/**
* {@inheritDoc}
*/
// @Override
// public List<ModuleDescriptor> parse(String name, String config, ParsingContext parsingContext) {
//
// StreamConfigParser parser = new StreamConfigParser(repository);
// StreamNode stream = parser.parse(name, config);
// Deque<ModuleDescriptor.Builder> builders = new LinkedList<ModuleDescriptor.Builder>();
//
// List<ModuleNode> moduleNodes = stream.getModuleNodes();
// for (int m = moduleNodes.size() - 1; m >= 0; m--) {
// ModuleNode moduleNode = moduleNodes.get(m);
// ModuleDescriptor.Builder builder =
// new ModuleDescriptor.Builder()
// .setGroup(name)
// .setModuleName(moduleNode.getName())
// .setModuleLabel(moduleNode.getLabelName())
// .setIndex(m);
// if (moduleNode.hasArguments()) {
// ArgumentNode[] arguments = moduleNode.getArguments();
// for (ArgumentNode argument : arguments) {
// builder.setParameter(argument.getName(), argument.getValue());
// }
// }
// builders.add(builder);
// }
//
// SourceChannelNode sourceChannel = stream.getSourceChannelNode();
// if (sourceChannel != null) {
// if (parsingContext.supportsNamedChannels()) {
// builders.getLast().setSourceChannelName(sourceChannel.getChannelName());
// }
// else {
// throw new StreamDefinitionException(config, sourceChannel.getStartPos(),
// NAMED_CHANNELS_UNSUPPORTED_HERE);
// }
// }
//
// SinkChannelNode sinkChannel = stream.getSinkChannelNode();
// if (sinkChannel != null) {
// if (parsingContext.supportsNamedChannels()) {
// builders.getFirst().setSinkChannelName(sinkChannel.getChannelName());
// }
// else {
// throw new StreamDefinitionException(config, sinkChannel.getChannelNode().getStartPos(),
// NAMED_CHANNELS_UNSUPPORTED_HERE);
// }
// }
//
// // Now that we know about source and sink channel names,
// // do a second pass to determine type. Also convert to composites.
// // And while we're at it (and type is known), validate module name and options
// List<ModuleDescriptor> result = new ArrayList<ModuleDescriptor>(builders.size());
// for (ModuleDescriptor.Builder builder : builders) {
// builder.setType(determineType(builder, builders.size() - 1, parsingContext));
//
// // definition is guaranteed to be non-null here
// ModuleDefinition moduleDefinition = moduleDefinitionRepository
// .findByNameAndType(builder.getModuleName(), builder.getType());
// builder.setModuleDefinition(moduleDefinition);
// ModuleOptionsMetadata optionsMetadata = moduleOptionsMetadataResolver.resolve(moduleDefinition);
// if (parsingContext.shouldBindAndValidate()) {
// try {
// optionsMetadata.interpolate(builder.getParameters());
// }
// catch (BindException e) {
// throw ModuleConfigurationException.fromBindException(builder.getModuleName(),
// builder.getType(), e);
// }
// }
//
// result.add(buildModuleDescriptor(builder));
// }
// return result;
// }
/**
* For a given module builder, determine the type of module based on:
* <ol>
* <li>module name</li>
* <li>module position in the stream</li>
* <li>presence of (or lack thereof) named channels</li>
* </ol>
*
* @param builder builder object
* @param lastIndex index of last module in the stream
* @param parsingContext parsing context
* @return module type
* @throws NoSuchModuleException if the module type does not exist
*/
// private ModuleType determineType(ModuleDescriptor.Builder builder, int lastIndex, ParsingContext parsingContext) {
// ModuleType moduleType = determineTypeFromNamedChannels(builder, lastIndex, parsingContext);
// if (moduleType != null) {
// return moduleType;
// }
// String name = builder.getModuleName();
// int index = builder.getIndex();
//
// return resolveModuleType(name, parsingContext.allowed(Position.of(index, lastIndex)));
// }
/**
* Attempt to guess the type of a module given the presence of named channels
* references at the start or end of the stream definition.
*
* @return module type, or null if no named channels were present
*/
// private ModuleType determineTypeFromNamedChannels(ModuleDescriptor.Builder builder, int lastIndex,
// ParsingContext parsingContext) {
// // Should this fail for composed module too?
// if (parsingContext == ParsingContext.job
// && (builder.getSourceChannelName() != null || builder.getSinkChannelName() != null)) {
// throw new RuntimeException("TODO");
// }
// ModuleType type = null;
// String moduleName = builder.getModuleName();
// int index = builder.getIndex();
// if (builder.getSourceChannelName() != null) { // preceded by >, so not a source
// if (index == lastIndex) { // this is the final module of the stream
// if (builder.getSinkChannelName() != null) { // but followed by >, so not a sink
// type = ModuleType.processor;
// }
// else { // final module and no >, so IS a sink
// type = ModuleType.sink;
// }
// }
// else { // not final module, must be a processor
// type = ModuleType.processor;
// }
// }
// else if (builder.getSinkChannelName() != null) { // followed by >, so not a sink
// if (index == 0) { // first module in a stream, and not preceded by >, so IS a source
// type = ModuleType.source;
// }
// else { // not first module, and followed by >, so not a source or sink
// type = ModuleType.processor;
// }
// }
// return (type == null) ? null : resolveModuleType(moduleName, type);
// }
/**
* Return a {@link org.springframework.xd.module.ModuleDescriptor}
* per the specifications indicated by the provided builder. If the module
* is a composite module, the children modules are also built and included
* under {@link org.springframework.xd.module.ModuleDescriptor#getChildren()}.
*
* @param builder builder object
* @return new instance of {@code ModuleDescriptor}
*/
// private ModuleDescriptor buildModuleDescriptor(ModuleDescriptor.Builder builder) {
// ModuleDefinition def = moduleDefinitionRepository.findByNameAndType(builder.getModuleName(), builder.getType());
// if (def != null && def.getDefinition() != null) {
// List<ModuleDescriptor> children = parse(def.getName(), def.getDefinition(), ParsingContext.module);
//
// // Preserve the options set for the "parent" module in the parameters map
// Map<String, String> parameters = new HashMap<String, String>(builder.getParameters());
//
// // Pretend that options were set on the composed module itself
// // (eases resolution wrt defaults later)
// for (ModuleDescriptor child : children) {
// for (String key : child.getParameters().keySet()) {
// String prefix = child.getModuleName() + CompositeModule.OPTION_SEPARATOR;
// builder.setParameter(prefix + key, child.getParameters().get(key));
// }
// }
//
// // This is to copy options from parent to this (which may override
// // what was set above)
// for (Map.Entry<String, String> entry : parameters.entrySet()) {
// builder.setParameter(entry.getKey(), entry.getValue());
// }
//
// // Since ModuleDescriptor is immutable, the children created
// // by the parse method above have to be recreated since the group
// // name needs to be modified
// List<ModuleDescriptor> list = new ArrayList<ModuleDescriptor>();
// for (ModuleDescriptor child : children) {
// ModuleDescriptor.Builder childBuilder =
// ModuleDescriptor.Builder.fromModuleDescriptor(child);
// childBuilder.setGroup(builder.getGroup() + "." + child.getModuleName());
// list.add(childBuilder.build());
// }
//
// builder.addChildren(list);
// }
// return builder.build();
// }
/**
* Return the module type for the module name. Based on the position
* in the stream definition, the module <b>must</b> be one of the
* types passed into {@code candidates}.
*
* @param moduleName name of module
* @param candidates the list of module types the module can be
* @return the module type for the module name
* @throws NoSuchModuleException if no module with this name exists for one of
* the types present in {@code candidates}
*/
// private ModuleType resolveModuleType(String moduleName, ModuleType... candidates) {
// for (ModuleType type : candidates) {
// ModuleDefinition def = moduleDefinitionRepository.findByNameAndType(moduleName, type);
// if (def != null) {
// return type;
// }
// }
// throw new NoSuchModuleException(moduleName, candidates);
// }
}