/******************************************************************************
* Copyright (C) 2015 Yevgeny Krasik *
* *
* 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 com.github.ykrasik.jaci.hierarchy;
import com.github.ykrasik.jaci.command.CommandDef;
import com.github.ykrasik.jaci.directory.CommandDirectoryDef;
import com.github.ykrasik.jaci.path.ParsedPath;
import com.github.ykrasik.jaci.reflection.ReflectionClassProcessor;
import com.github.ykrasik.jaci.reflection.ReflectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
/**
* A definition for a hierarchy of {@link CommandDirectoryDef}s and {@link CommandDef}s.
* Essentially, this is a file system starting from a single 'root' directory.
* Built through the {@link CommandHierarchyDef.Builder} builder.
*
* @author Yevgeny Krasik
*/
public class CommandHierarchyDef {
private final CommandDirectoryDef root;
private CommandHierarchyDef(CommandDirectoryDef root) {
this.root = Objects.requireNonNull(root, "root");
}
/**
* @return Root {@link CommandDirectoryDef}.
*/
public CommandDirectoryDef getRoot() {
return root;
}
/**
* A builder for a {@link CommandHierarchyDef}.
*/
public static class Builder {
private final ReflectionClassProcessor processor = new ReflectionClassProcessor();
private final CommandDirectoryDef.Builder root = new CommandDirectoryDef.Builder("root").setDescription("root");
/**
* Process the classes and add any commands defined through annotations to this builder.
* Each class must have a no-args constructor.
*
* @param classes Classes to process.
* @return {@code this}, for chaining.
*/
public Builder processClasses(Class<?>... classes) {
for (Class<?> clazz : classes) {
doProcessClass(clazz);
}
return this;
}
private void doProcessClass(Class<?> clazz) {
final Object instance = ReflectionUtils.createInstanceNoArgs(clazz);
doProcess(instance);
}
/**
* Process the objects' classes and add any commands defined through annotations to this builder.
*
* @param instances Objects whose classes to process.
* @return {@code this}, for chaining.
*/
public Builder process(Object... instances) {
for (Object instance : instances) {
doProcess(instance);
}
return this;
}
private void doProcess(Object instance) {
final Map<ParsedPath, List<CommandDef>> pathToCommandDefsMap = processor.processObject(instance);
// Add the returned commands to the hierarchy.
for (Entry<ParsedPath, List<CommandDef>> entry : pathToCommandDefsMap.entrySet()) {
addCommandDefs(entry.getKey(), entry.getValue());
}
}
private void addCommandDefs(ParsedPath path, List<CommandDef> commandDefs) {
// Advance along the path, creating directories as necessary.
CommandDirectoryDef.Builder dir = root;
for (String name : path) {
dir = dir.getOrCreateDirectory(name);
}
dir.addCommandDefs(commandDefs);
}
/**
* @return A {@link CommandHierarchyDef} built out of this builder's parameters.
*/
public CommandHierarchyDef build() {
final CommandDirectoryDef rootDef = root.build();
return new CommandHierarchyDef(rootDef);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Builder{");
sb.append("root=").append(root);
sb.append('}');
return sb.toString();
}
}
}