All Downloads are FREE. Search and download functionalities are using the official Maven repository.

protoj.lang.InstructionChain Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2009 Ashley Williams
 * 
 * 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 protoj.lang;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;

import protoj.lang.internal.InstructionChainArgs;
import protoj.util.ArgRunnable;

/**
 * Represents the list of instruction that are to be executed, that are usually
 * parsed from the command line. However instructions may be programmatically
 * added with the {@link #addInstruction(String, String)} method. The
 * instructions are separated by spaces and consist of a name with possible
 * options. Each instruction can also contain spaces when options are specified
 * and so when this is the case, quotes be used around the entire instruction.
 * The following example is an chain of two instructions:
 * 
 * 
 * command1 "command2 -optA -optB"
 * 
* * The first instruction consists of just a name whereas the second instruction * consists of a name as well as two options. *

* Most of the instructions correspond to commands with the same name from the * commandStore that should be executed. However there are three special * instructions that need specific handling: *

    *
  • {@link Instruction#INIT} instruction: provides initialization information * for the protoj domain objects including the project root directory and script * name used to start the application.
  • *
  • {@link Instruction#OPTS} instruction: provides system properties to the * application
  • *
* * @author Ashley Williams * */ public final class InstructionChain { /** * The command instructions ready to be processed. */ private LinkedHashMap commandInstructions; /** * See {@link #getJvmArgs()}. */ private List jvmArgs; /** * See {@link #getRootDir()}. */ private File rootDir; /** * See {@link #getScriptName()}. */ private String scriptName; /** * See {@link #getInitInstruction()}. */ private Instruction initInstruction; /** * See {@link #getOptsInstruction()}. */ private Instruction optsInstruction; /** * See {@link #breakVisit()}. */ private boolean breakVisit; /** * Creates an instance based on command line arguments. * * @param args */ public InstructionChain(String[] args) { new InstructionChainArgs(this, args).initChain(); } /** * Alternative constructor that doesn't rely on a main args array. * * @param rootDir * @param scriptName */ public InstructionChain(File rootDir, String scriptName) { init(rootDir, scriptName); } /** * Common initialization method. Called by the constructors as well as by * the InstructionChainArgs helper class. * * @param rootDir * @param scriptName */ public void init(File rootDir, String scriptName) { this.rootDir = rootDir.getCanonicalFile(); this.scriptName = scriptName; this.commandInstructions = new LinkedHashMap(); this.jvmArgs = new ArrayList(); } /** * See {@link #addJvmArg(String)}. * * @return */ public List getJvmArgs() { return Collections.unmodifiableList(jvmArgs); } /** * Adds an additional jvm arg that will be applied to each command that is * started in a new vm during the call to * {@link DispatchFeature#startVm(String, String[], String, ArgRunnable)}. * The specified jvmArg should be in -DX=Y format. * * @param jvmArg */ public void addJvmArg(String jvmArg) { jvmArgs.add(jvmArg); } /** * Adds another item to the configuration. A single item may contain not * just the name but also the options, for example: * "mycommand -arg1 -arg2". The first word in each instruction * string when spaces are used as separators is assumed to be the name of * the instruction. *

* The special init and opts instructions are kept out of the * commandInstructions property since they don't correspond to commands in * the command store. Instead they receive special treatment and are * assigned to the initInstruction and optsInstruction properties instead. *

* If an instruction of the same name already exists it will be replaced. * This limitation may be revisited in future. * * @param name * @param options * @return */ public Instruction addInstruction(String name, String options) { Instruction instruction = new Instruction(name, options); if (instruction.isInitInstruction()) { initInstruction = instruction; } else if (instruction.isOptsInstruction()) { optsInstruction = instruction; } else { commandInstructions.put(name, instruction); } return instruction; } /** * The init definition that should always be present. * * @return */ public Instruction getInitInstruction() { return initInstruction; } /** * The opts definition that may be null. * * @return */ public Instruction getOptsInstruction() { return optsInstruction; } /** * Removes the instruction with the given name and returns it. Null is * returned if no matching instruction is found. * * @param name * @return */ public Instruction remove(String name) { return commandInstructions.remove(name); } /** * Visits each of the instructions in turn, removing them as they are * encountered. This is because the assumption is that the commands are * being handled one at a time and don't want to risk them being rehandled. * In order to stop the visit early, the visitor should call * {@link #breakVisit()}. This will mean that it doesn't get passed any * further instructions, but they are all still removed by the time this * method returns. * * @param visitor */ public void visitAndRemove(ArgRunnable visitor) { breakVisit = false; ArrayList instructions = new ArrayList(); instructions.addAll(commandInstructions.values()); // iterate over a copy of the instruction values and remove the whole // hashmap entry each time during the iteration Iterator values = instructions.listIterator(); while (values.hasNext() && !breakVisit) { Instruction instruction = values.next(); visitor.run(instruction); values.remove(); remove(instruction.getName()); } commandInstructions.clear(); } /** * See {@link #visitAndRemove(ArgRunnable)}. * */ public void breakVisit() { this.breakVisit = true; } /** * Creates command line args suitable for passing in a main method, * including all the command instructions in this chain. Delegates to * {@link #createMainArgsAsArray(Collection)}. The command instructions are * then removed since the assumption is that a new vm will be started to * handle them instead. We don't want to handle them again in the current * vm. * * @param skipFirst * specify true if the first command instruction isn't required - * useful when bootstrapping commands not including the current * @return */ public String[] createArgsAndRemove(boolean skipFirst) { Collection values; if (skipFirst) { ArrayList skippedList = new ArrayList(); skippedList.addAll(commandInstructions.values()); skippedList.remove(0); values = skippedList; } else { values = commandInstructions.values(); } String[] createMainArgs = createMainArgsAsArray(values); // TODO can't always clear since may be inside visitAndRemove() iterator // commandInstructions.clear(); return createMainArgs; } /** * Creates command line args suitable for passing in a main method using the * specified list of instruction commands. The resulting returned array * contains the init instruction as the first element and the opts * instruction, if any, as the last element. *

* Note that if this is called by a visitor to * {@link #visitAndRemove(ArgRunnable)} then the returned array will include * the current instruction since it doesn't get removed until after the * visit. * * @param commandInstructions * @return */ private String[] createMainArgsAsArray( Collection commandInstructions) { ArrayList args = new ArrayList(); args.add(getInitInstruction().getText()); boolean containsDefinitions = commandInstructions.size() > 0; if (containsDefinitions) { for (Instruction definition : commandInstructions) { args.add(definition.getText()); } } if (getOptsInstruction() != null) { args.add(getOptsInstruction().getText()); } return args.toArray(new String[] {}); } /** * Creates command line args suitable for passing in a main method, * including just the specified instruction name and opts parameters. * Delegates to {@link #createMainArgsAsArray(Collection)}. * * @param name * @param opts * @return */ public String[] createMainArgs(String name, String opts) { Instruction command = new Instruction(name, opts); ArrayList commands = new ArrayList(); commands.add(command); return createMainArgsAsArray(commands); } /** * The project root directory. * * @return */ public File getRootDir() { return rootDir; } /** * The name of the script that invokes this application. * * @return */ public String getScriptName() { return scriptName; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy