Path: blob/master/sourcetools/com.ibm.jpp.preprocessor/com/ibm/jpp/om/ConfigObject.java
6004 views
/*******************************************************************************1* Copyright (c) 1999, 2019 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/21package com.ibm.jpp.om;2223import java.io.File;24import java.util.ArrayList;25import java.util.Enumeration;26import java.util.HashMap;27import java.util.HashSet;28import java.util.Iterator;29import java.util.List;30import java.util.Map;31import java.util.Map.Entry;32import java.util.Properties;33import java.util.Set;3435import com.ibm.jpp.commandline.CommandlineLogger;36import com.ibm.jpp.commandline.CommandlineNowarnLogger;3738/**39* A config object is a preprocessor configuration used to generate a version of the40* J9 Java Class Libraries (JCL).41* <p>42* A <code>ConfigObject</code> identifies all of the flags associated with said preprocessor43* configuration and at the same time defines the output path, source paths, classpath entries,44* and dependent jobs. Dependent jobs are <code>ConfigObject</code> dependencies that need to45* be preprocessed before and that inherit the options and output path of the parent.46* <p>47* Furthermore, <code>ConfigObject</code>s are collected in a {@link ConfigurationRegistry} which48* guarantees name uniqueness. Note that name uniqueness is not guaranteed across registryies.49*50* @see com.ibm.jpp.om.ConfigurationRegistry51* @see com.ibm.jpp.om.MetaRegistry52* @see com.ibm.jpp.om.Src53*/54public class ConfigObject {55private final String name;56private final boolean isConfiguration; // If this is a configuration or a set57private String outputPath;58private String outputPathKeyword;5960private String testsTempOutputPath;61private String testsOutputPath;62private String testsTempBootOutputPath;63private String testsBootOutputPath;6465private final Map<String, String> defaultTestsResources;66private final Map<String, String> testsResources;67private boolean useTestResourcesDefaults = false;6869private String baseDir;70private final Set<String> addDependFlagSet;71private final Set<String> removeDependFlagSet;72private final Set<String> requiredIncludeFlagSet;73private final Set<String> flagSet;74private final Set<String> removeFlagSet;75private String[] flagArray = null;76private final List<String> optionSet; // The order matters, pairs of name/value77private final List<String> compilerOptionSet; // The order matters, pairs of name/value78private final List<Src> sources;79private final List<ClassPathEntry> classPathEntries;80private final Map<String, ConfigObject> dependJobs;81private String JDKCompliance;82private boolean metadata = false;83/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */84// prefixes and suffixes85private String outputPathPrefix = "";86private String outputPathSuffix = "";87private String testsOutputPathPrefix = "";88private String testsOutputPathSuffix = "";89private String bootTestsOutputPathPrefix = "";90private String bootTestsOutputPathSuffix = "";9192/* [PR 120359] New classpath entry is needed for configurations */93private String configXMLParserError = "";94// Registry containing this ConfigObject (aka parent registry)95private ConfigurationRegistry registry;9697/**98* Class constructor.99*100* @param name the configuration's name101* @param isConfiguration <code>true</code> if this is a configuration, <code>false</code> if this is a set102*/103public ConfigObject(String name, boolean isConfiguration) {104this(name, "", isConfiguration);105}106107/**108* Class constructor.109*110* @param name the configuration's name111* @param baseDir the base directory for this configuration112* @param isConfiguration <code>true</code> if this is a configuration, <code>false</code> if this is a set113*/114public ConfigObject(String name, String baseDir, boolean isConfiguration) {115this.name = name;116this.baseDir = baseDir;117this.isConfiguration = isConfiguration;118119flagSet = new HashSet<>();120requiredIncludeFlagSet = new HashSet<>();121removeFlagSet = new HashSet<>();122addDependFlagSet = new HashSet<>();123removeDependFlagSet = new HashSet<>();124125classPathEntries = new ArrayList<>();126optionSet = new ArrayList<>();127compilerOptionSet = new ArrayList<>();128sources = new ArrayList<>();129dependJobs = new HashMap<>();130/* [PR 121295] Support for resources' prefixes and directories */131defaultTestsResources = new HashMap<>();132testsResources = new HashMap<>();133}134135/**136* Returns whether this ConfigObject is a configuration or not.137*138* @return <code>true</code> if this config object is a configuration, <code>false</code> otherwise139*140* @see #isSet()141*/142public boolean isConfiguration() {143return isConfiguration;144}145146/**147* Returns whether this ConfigObject is a set or not.148*149* @return <code>true</code> if this config object is a set, <code>false</code> otherwise150*151* @see #isConfiguration()152*/153public boolean isSet() {154return !isConfiguration;155}156157/**158* Returns this ConfigObject's name (or label).159*160* @return the name161*/162public String getName() {163return name;164}165166/**167* Returns whether or not tests can be generated for this config.168*169* @return <code>true</code> if tests can be generated, <code>false</code> otherwise170*/171public boolean canGenerateTests() {172return (registry.getTestsProject() != null && !registry.getTestsProject().equals(""));173}174175/**176* Sets this ConfigObject's JDK Compiler compliance version.177*178* @param version the JDK version179*/180public void setJDKCompliance(String version) {181JDKCompliance = version;182}183184/**185* Returns this ConfigObject's JDK Compiler compliance version.186*187* @return the JDK compliance version188*/189public String getJDKCompliance() {190return JDKCompliance;191}192193/**194* Sets whether or not preprocessor metadata will be generated for this config.195*196* @param metadata <code>true</code> if metadata is to be generated,197* <code>false</code> otherwise198*/199public void enableMetadata(boolean metadata) {200this.metadata = metadata;201}202203/**204* Returns whether or not preprocessor metadata is enabled.205*206* @return <code>true</code> if metadata will be written, <code>false</code> otherwise207*/208public boolean isMetadataEnabled() {209return metadata;210}211212/**213* Adds a classpath entry to this ConfigObject. This classpath entry will be214* used by the eclipse plugin to generate a project classpath.215*216* @param path the classpath217* @param type the type of classpath218* @param isExported <code>true</code> if this entry is contributed to dependent projects, <code>false</code> otherwise219*/220public void addClassPathEntry(String path, String type, boolean isExported) {221classPathEntries.add(new ClassPathEntry(path, type, isExported));222}223224/**225* Adds a classpath entry to this ConfigObject. This classpath entry will be226* used by the eclipse plugin to generate a project classpath.227*228* @param path the classpath229* @param type the type of classpath230* @param sourcePath the classpath's source path231* @param isExported <code>true</code> if this entry is contributed to dependent projects, <code>false</code> otherwise232*/233public void addClassPathEntry(String path, String type, String sourcePath, boolean isExported) {234classPathEntries.add(new ClassPathEntry(path, type, sourcePath, isExported));235}236237/**238* Adds a classpath entry to this ConfigObject. This classpath entry will be239* used by the eclipse plugin to generate a project classpath.240*241* @param classpath the classpath entry242*/243public void addClassPathEntry(ClassPathEntry classpath) {244classPathEntries.add(classpath);245}246247/**248* Returns this ConfigObject's classpath entries.249*250* @return the classpath entries251*/252public ClassPathEntry[] getClassPathEntries() {253return classPathEntries.toArray(new ClassPathEntry[classPathEntries.size()]);254}255256/**257* Adds a flag to this ConfigObject.258*259* @param flag the flag to be added260*/261public void addFlag(String flag) {262flagSet.add(flag);263}264265/* [PR 117967] idea 491: Automatically create the jars required for test bootpath */266/**267* Adds a flag to removeFlagSet.268*269* @param flag the flag to be added270*/271public void addFlagToRemoveFlagSet(String flag) {272removeFlagSet.add(flag);273}274275/**276* Removes a flag from this ConfigObject.277*278* @param flag the flag to be removed279*/280public void removeFlag(String flag) {281flagSet.remove(flag);282}283284/* [PR 117967] idea 491: Automatically create the jars required for test bootpath */285/**286* Removes a flag from requiredflagset287*288* @param flag the flag to be removed289*/290public void removeRequiredFlag(String flag) {291requiredIncludeFlagSet.remove(flag);292}293294/**295* Returns the flags associated with this ConfigObject.296* <p>297* <b>Note:</b> the flagSet isn't completely correct until updateWithDependencies298* is called, when the removes and adding of sets happens299*300* @return the ConfigObject flags301*/302public Set<String> getFlags() {303return flagSet;304}305306/**307* Returns the flags associated with this ConfigObject.308* <p>309* <b>Note:</b> the flagSet isn't completely correct until updateWithDependencies310* is called, when the removes and adding of sets happens311*312* @return the ConfigObject flags313*314* @see #getFlags()315*/316public String[] getFlagsAsArray() {317if (flagArray == null || flagArray.length != flagSet.size()) {318flagArray = new String[flagSet.size()];319flagSet.toArray(flagArray);320}321return flagArray;322}323324/**325* Adds a flag dependency to another configuration or set326*327* @param name the dependency name328*/329public void addFlagDependency(String name) {330addDependFlagSet.add(name);331}332333/**334* Removes a flag dependency to another configuration or set335*336* @param name the dependency name337*/338public void removeFlagDependency(String name) {339removeDependFlagSet.add(name);340}341342/**343* Adds a new required include flag to this ConfigObject.344*345* @param newFlag the new flag to be added.346*/347public void addRequiredIncludeFlag(String newFlag) {348requiredIncludeFlagSet.add(newFlag);349}350351/**352* Returns the required include flags.353*354* @return the required include flags355*/356public Set<String> getRequiredIncludeFlagSet() {357return requiredIncludeFlagSet;358}359360/**361* Adds a dependency on another ConfigObject, which then must be preprocessed362* first.363*364* @param dependName the name of the dependent ConfigObject365* @param config the dependent ConfigObject366*/367public void addDependJob(String dependName, ConfigObject config) {368dependJobs.put(dependName, config);369}370371/**372* Returns this ConfigObject's complete configuration dependencies through recursion.373*374* @return the configuration dependencies375*/376public Map<String, ConfigObject> getAllDependJobs() {377Map<String, ConfigObject> allDependencies = new HashMap<>();378allDependencies.putAll(dependJobs);379380// Recursively get the configuration dependencies381if (!dependJobs.isEmpty()) {382ConfigObject[] dependConfigs = dependJobs.values().toArray(new ConfigObject[dependJobs.size()]);383384for (ConfigObject dependConfig : dependConfigs) {385if (dependConfig != null) {386allDependencies.putAll(dependConfig.getDependJobs());387}388}389}390391return allDependencies;392}393394/**395* Returns this ConfigObject's immediate configuration dependencies.396*397* @return the configuration dependencies398*/399public Map<String, ConfigObject> getDependJobs() {400return dependJobs;401}402403/**404* Returns this ConfigObject's parent registry405*406* @return the parent registry407*/408public ConfigurationRegistry getRegistry() {409return registry;410}411412/**413* Sets this ConfigObject's parent registry414*415* @param registry the registry to set416*/417public void setRegistry(ConfigurationRegistry registry) {418this.registry = registry;419}420421/**422* Checks whether this ConfigObject has flag dependencies that need to be423* resolved with {@link #updateWithDependencies()}.424*425* @return <code>true</code> if there are flag dependencies to be resolved, <code>false</code> otherwise426*/427public boolean checkDependencies() {428return (!addDependFlagSet.isEmpty() && !removeDependFlagSet.isEmpty()) ? true : registry.getConfigurationNamesAsSet().containsAll(addDependFlagSet);429}430431/**432* Updates the flag dependencies in this ConfigObject.433*/434public void updateWithDependencies() {435for (String configName : addDependFlagSet) {436ConfigObject configuration = registry.getConfiguration(configName);437flagSet.addAll(configuration.getFlags());438requiredIncludeFlagSet.addAll(configuration.getRequiredIncludeFlagSet());439}440441for (String configName : removeDependFlagSet) {442ConfigObject configuration = registry.getConfiguration(configName);443flagSet.removeAll(configuration.getFlags());444requiredIncludeFlagSet.removeAll(configuration.getRequiredIncludeFlagSet());445}446447flagSet.removeAll(removeFlagSet);448requiredIncludeFlagSet.removeAll(removeFlagSet);449}450451/**452* Adds global options to this ConfigObject. These options will apply to all453* sources.454*455* @param name the option name456* @param value the option value457*/458public void addOption(String name, String value) {459optionSet.add(name);460optionSet.add(value);461}462463/* [PR 119500] Design 955 Core.JCL : Support bootpath JCL testing */464/**465* Removes a global option from this ConfigObject. These options will apply to all466* sources.467*468* @param name the option name469*/470public void removeOption(String name) {471for (int i = 0; i < optionSet.size();) {472String option = optionSet.get(i);473if (option.equals(name)) {474optionSet.remove(i);475optionSet.remove(i);476} else {477i += 2;478}479}480}481482/* [PR 118829] Desing 894: Core.JCL : Support for compiler options in preprocessor plugin */483/**484* Adds compiler options to this ConfigObject. These options will apply to485* config projects' compiler options486*487* @param name the option name488* @param value the option value489*/490public void addCompilerOption(String name, String value) {491compilerOptionSet.add(name);492compilerOptionSet.add(value);493}494495/**496* Returns the global options associated with this ConfigObject.497*498* @return the global options499*/500public Properties getOptions() {501Properties options = new Properties();502int setSize = optionSet.size();503for (int i = 0; i < setSize; i += 2) {504options.setProperty(optionSet.get(i), optionSet.get(i + 1));505}506return options;507}508509/**510* Returns the compiler options associated with this ConfigObject.511*512* @return the compiler options513*/514public Properties getCompilerOptions() {515Properties coptions = new Properties();516int setSize = compilerOptionSet.size();517for (int i = 0; i < setSize; i += 2) {518coptions.setProperty(compilerOptionSet.get(i), compilerOptionSet.get(i + 1));519}520return coptions;521}522523/**524* Sets this ConfigObject's base directory.525*526* @param newBaseDir the new base dir527*/528public void setBaseDir(String newBaseDir) {529baseDir = newBaseDir;530}531532/**533* Returns this ConfigObject's base directory.534*535* @return the base directory536*/537public String getbaseDir() {538return baseDir;539}540541/**542* Sets the outputPath keyword to get output project names.543* @param outputPath outpath value in jpp_configuration.xml544*/545/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */546public void setOutputPathKeyword(String outputPath) {547if (!outputPath.startsWith(File.separator) && !outputPath.startsWith(":", 1)) {548int index = Math.max(outputPath.indexOf("\\"), outputPath.indexOf("/"));549if (index != -1) {550outputPathKeyword = outputPath.substring(0, index).trim();551} else {552outputPathKeyword = outputPath.trim();553}554}555}556557/**558* outputPath name without prefix and suffix559* @return outputPathKeyword560*/561/* [PR 120359] New classpath entry is needed for configurations */562public String getOutputPathKeyword() {563return outputPathKeyword;564}565566/**567* Sets this ConfigObject's output path568*569* @param newOutputPath the new output path570*/571public void setOutputPath(String newOutputPath) {572if (!newOutputPath.startsWith(File.separator) && !newOutputPath.startsWith(":", 1)) {573StringBuffer buffer = new StringBuffer(baseDir);574buffer.append(outputPathPrefix + outputPathKeyword + outputPathSuffix + "/src");575outputPath = buffer.toString();576} else {577outputPath = newOutputPath;578}579}580581/**582* Sets this ConfigObject's tests output path583*584*/585/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */586public void setTestsOutputPaths() {587if (outputPathKeyword != null) {588testsTempOutputPath = baseDir + testsOutputPathPrefix + outputPathKeyword + testsOutputPathSuffix + "/preprocessedTests";589testsOutputPath = baseDir + testsOutputPathPrefix + outputPathKeyword + testsOutputPathSuffix + "/src";590} else {591int lastIndex = Math.max(outputPath.lastIndexOf("\\"), outputPath.lastIndexOf("/"));592String token = outputPath.substring(0, lastIndex);593testsTempOutputPath = token + " Tests/preprocessedTests";594testsOutputPath = token + " Tests/src";595}596}597598/**599* Sets this ConfigObject's boot tests output path600*601*/602/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */603public void setBootTestsOutputPaths() {604if (outputPathKeyword != null) {605testsTempBootOutputPath = baseDir + bootTestsOutputPathPrefix + outputPathKeyword + bootTestsOutputPathSuffix + "/preprocessedBootPathTests";606testsBootOutputPath = baseDir + bootTestsOutputPathPrefix + outputPathKeyword + bootTestsOutputPathSuffix + "/src";607} else {608int lastIndex = Math.max(outputPath.lastIndexOf("\\"), outputPath.lastIndexOf("/"));609String token = outputPath.substring(0, lastIndex);610testsTempBootOutputPath = token + " Tests BootPath/preprocessedBootPathTests";611testsBootOutputPath = token + " Tests BootPath/src";612}613}614615/* [PR 117967] idea 491: Automatically create the jars required for test bootpath */616public void setOutputPathforJCLBuilder(String newOutputPath) {617String path;618if (!newOutputPath.startsWith(File.separator) && !newOutputPath.startsWith(":", 1)) {619StringBuffer buffer = new StringBuffer(baseDir);620buffer.append(newOutputPath);621path = buffer.toString();622} else {623path = newOutputPath;624}625626outputPath = path;627testsTempOutputPath = path;628testsTempBootOutputPath = path;629}630631/* [PR 117967] idea 491: Automatically create the jars required for test bootpath */632/**633* Returns this ConfigObject's tests boot path634*/635public String getTestsBootPath() {636return testsTempBootOutputPath;637}638639/* [PR 118220] Incremental builder is not called when file is deleted in base library */640/**641* Returns this ConfigObject's tests Outputpath642*/643public String getTestsOutputPath() {644return testsOutputPath;645}646647/**648* Returns this ConfigObject's tests Outputpath649*/650/* [PR 119753] classes.txt and AutoRuns are not updated when new test class is added */651public String getBootTestsOutputPath() {652return testsBootOutputPath;653}654655/**656* Returns this ConfigObject's outputPathPrefix657*/658/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */659public String getOutputPathPrefix() {660return outputPathPrefix;661}662663/**664* Returns this ConfigObject's outputPathSuffix665*/666/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */667public String getOutputPathSuffix() {668return outputPathSuffix;669}670671/**672* Returns this ConfigObject's testsOutputPathPrefix673*/674/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */675public String getTestsOutputPathPrefix() {676return testsOutputPathPrefix;677}678679/**680* Returns this ConfigObject's testsOutputPathSuffix681*/682/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */683public String getTestsOutputPathSuffix() {684return testsOutputPathSuffix;685}686687/**688* Returns this ConfigObject's bootTestsOutputPathPrefix689*/690/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */691public String getBootTestsOutputPathPrefix() {692return bootTestsOutputPathPrefix;693}694695/**696* Returns this ConfigObject's bootTestsOutputPathSuffix697*/698/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */699public String getBootTestsOutputPathSuffix() {700return bootTestsOutputPathSuffix;701}702703/**704* Sets this ConfigObject's OutputPath Prefix705*/706/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */707public void setOutputPathPrefix(String prefix) {708outputPathPrefix = prefix;709}710711/**712* Sets this ConfigObject's OutputPath Suffix713*/714public void setOutputPathSuffix(String suffix) {715outputPathSuffix = suffix;716}717718/**719* Sets this ConfigObject's Tests OutputPath Prefix720*/721public void setTestsOutputPathPrefix(String prefix) {722testsOutputPathPrefix = prefix;723}724725/**726* Sets this ConfigObject's Tests OutputPath Suffix727*/728public void setTestsOutputPathSuffix(String suffix) {729testsOutputPathSuffix = suffix;730}731732/**733* Sets this ConfigObject's Boot Tests OutputPath Prefix734*/735public void setBootTestsOutputPathPrefix(String prefix) {736bootTestsOutputPathPrefix = prefix;737}738739/**740* Sets this ConfigObject's Boot Tests OutputPath Suffix741*/742public void setBootTestsOutputPathSuffix(String suffix) {743bootTestsOutputPathSuffix = suffix;744}745746/**747* Sets this ConfigObject's tests boot path748*/749public void setTestsBootPath(String bootpath) {750testsTempBootOutputPath = bootpath;751}752753/**754* Sets the global resources prefixes to this config object755* @param res resources prefixes756*/757/* [PR 121295] Support for resources' prefixes and directories */758public void setDefaultTestsResourcesPrefixes(Map<String, String> res) {759defaultTestsResources.putAll(res);760if (defaultTestsResources.keySet().size() == 0) {761defaultTestsResources.put("*", "j9ts_");762}763}764765/**766* Sets the config objects's resources prefix. It parses all resources' prefixes and removes the prefixes that are overwritten.767* @param dir Directory768* @param prefix Prefix of resources769*/770/* [PR 121295] Support for resources' prefixes and directories */771public void setTestsResourcesPrefix(String dir, String prefix) {772if (!dir.equals("*")) {773String dirSlash = dir + "/";774775for (Iterator<String> iter = testsResources.keySet().iterator(); iter.hasNext();) {776String nextDir = iter.next();777778if (nextDir.startsWith(dirSlash) || nextDir.equals(dir)) {779iter.remove();780}781}782} else {783testsResources.clear();784}785testsResources.put(dir, prefix);786}787788public void setUseTestResourcesDefaults(boolean def) {789useTestResourcesDefaults = def;790}791792public Map<String, String> getTestsResourcesPrefixes() {793Map<String, String> prefixes = new HashMap<>();794if (useTestResourcesDefaults) {795prefixes.putAll(defaultTestsResources);796797for (String dir : testsResources.keySet()) {798String dirSlash = dir + "/";799800for (Iterator<String> iter = prefixes.keySet().iterator(); iter.hasNext();) {801String nextDir = iter.next();802803if (nextDir.startsWith(dirSlash) || nextDir.equals(dir)) {804iter.remove();805}806}807}808prefixes.putAll(testsResources);809} else if (testsResources.keySet().size() != 0) {810prefixes = testsResources;811} else {812prefixes = defaultTestsResources;813}814return prefixes;815}816817/**818* Returns this ConfigObject's output path.819*820* @return the output path821*822* @see #getOutputDir()823*/824public String getOutputPath() {825return outputPath;826}827828/**829* Returns this ConfigObject's output path relative to the base directory.830*831* @return the relative output path832*/833public String getRelativeOutputPath() {834int pathLength = (baseDir != null && outputPath.length() > baseDir.length()) ? baseDir.length() : 0;835return (outputPath != null) ? outputPath.substring(pathLength) : null;836}837838/**839* Returns this ConfigObject's tests output path relative to the base directory.840*841* @return the relative tests output path842*/843/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */844public String getRelativeTestsOutputPath() {845int pathLength = (baseDir != null && testsOutputPath.length() > baseDir.length()) ? baseDir.length() : 0;846return (testsOutputPath != null) ? testsOutputPath.substring(pathLength) : null;847}848849/**850* Returns this ConfigObject's boot tests output path relative to the base directory.851*852* @return the relative boo853* ttests output path854*/855/* [PR 119756] config project prefix and postfix support in jpp_configuration.xml file */856public String getRelativeBootTestsOutputPath() {857int pathLength = (baseDir != null && testsBootOutputPath.length() > baseDir.length()) ? baseDir.length() : 0;858return (testsBootOutputPath != null) ? testsBootOutputPath.substring(pathLength) : null;859}860861/**862* Returns this ConfigObject's output directory.863*864* @return the output dir865*866* @see #getOutputPath()867* @see #getRelativeOutputPath()868*/869public File getOutputDir() {870return (outputPath != null) ? new File(outputPath) : null;871}872873/**874* Adds a config source to this ConfigObject.875*876* @param newSource the new config source877*/878public void addSource(Src newSource) {879sources.add(newSource);880}881882/**883* Returns the number of sources defined for this ConfigObject.884*885* @return the number of defined sources886*/887public int getSourceCount() {888return sources.size();889}890891/**892* Returns the sources defined for this ConfigObject.893*894* @return the config sources895*/896public Src[] getSources() {897return sources.toArray(new Src[sources.size()]);898}899900/**901* Returns this ConfigObject's source paths relative to the base directory.902*903* @return the relative source paths904*905* @see #getRelativeSrcDirs()906*/907public String[] getRelativeSrcPaths() {908String[] returnArray = new String[sources.size()];909for (int i = 0; i < returnArray.length; i++) {910returnArray[i] = sources.get(i).getRelativeSrcPath();911}912return returnArray;913}914915/**916* Returns this ConfigObject's source directories relative to the base directory.917*918* @return the relative source dirs919*920* @see #getRelativeSrcPaths()921*/922public File[] getRelativeSrcDirs() {923File[] returnArray = new File[sources.size()];924for (int i = 0; i < returnArray.length; i++) {925returnArray[i] = sources.get(i).getRelativeSrcFolder();926}927return returnArray;928}929930/**931* Returns this ConfigObject's absolute source directories.932*933* @return the absolute source dirs934*/935public String[] getAbsoluteSrcPaths() {936String[] returnArray = new String[sources.size()];937for (int i = 0; i < returnArray.length; i++) {938returnArray[i] = baseDir + sources.get(i).getRelativeSrcPath();939}940return returnArray;941}942943/**944* Returns this ConfigObject's absolute source directories.945*946* @return the absolute source dirs947*/948public File[] getAbsoluteSrcDirs() {949File[] returnArray = new File[sources.size()];950for (int i = 0; i < returnArray.length; i++) {951returnArray[i] = new File(baseDir + sources.get(i).getRelativeSrcPath());952}953return returnArray;954}955956/**957* Preprocesses this configuration object. If the ConfigObject has any config dependencies,958* those will be preprocessed first (automatically).959* <p>960* <b>NOTE:</b> the dependencies will inherit the configuration options and the output961* directory of the parent.962*963* @param incremental <code>true</code> if this build is incremental964* @param noWarn <code>true</code> if a logger with no warnings is to be used965*/966public boolean build(boolean incremental, boolean noWarn) {967return build(incremental, noWarn, new BuilderExtension[0], null);968}969970/* [PR 121491] Use correct build method and correct commandline input values for preprocessing jobs */971public boolean buildTests(boolean incremental, boolean noWarn) {972return buildTests(incremental, noWarn, new BuilderExtension[0], null);973}974975/* [PR 121584]Add option to jcl-builder to use testsBootPath JavaDoc */976public boolean buildTestBootpath(boolean incremental, boolean noWarn, boolean useTestBootpathJavaDoc) {977return buildTestBootpath(incremental, noWarn, useTestBootpathJavaDoc, new BuilderExtension[0], null, false);978}979980/**981* Preprocesses this configuration object. If the ConfigObject has any config dependencies,982* those will be preprocessed first (automatically).983* <p>984* <b>NOTE:</b> the dependencies will inherit the configuration options and the output985* directory of the parent.986*987* @param incremental <code>true</code> if this build is incremental988* @param noWarn <code>true</code> if a logger with no warnings is to be used989* @param extensions builder extensions to be added to the preprocess990* @param logger the build logger to use, if <code>null</code> one will be created991*/992public boolean build(boolean incremental, boolean noWarn, BuilderExtension[] extensions, Logger logger) {993// Preprocess all dependent jobs first994if (!dependJobs.isEmpty()) {995for (Entry<String, ConfigObject> entry : dependJobs.entrySet()) {996ConfigObject dependConfig = entry.getValue();997998if (dependConfig != null) {999Properties options = this.getOptions();1000Enumeration<?> keys = options.keys();1001while (keys.hasMoreElements()) {1002String keyName = keys.nextElement().toString();1003dependConfig.addOption(keyName, options.getProperty(keyName));1004}10051006// requires setting the destDir1007String tempOutputPath = dependConfig.getOutputPath();1008dependConfig.setOutputPath(this.getOutputPath());1009dependConfig.build(incremental, noWarn, extensions, logger);10101011// Return the output path to its initial value1012dependConfig.setOutputPath(tempOutputPath);1013} else {1014System.err.println("\nCould not find dependent configuration: " + entry.getKey());1015}1016}1017}10181019boolean result = true;1020int multipleSources = 0;1021// Preprocess this ConfigObject1022for (int i = 0; i < sources.size(); ++i) {1023Src source = sources.get(i);1024File srcPath = new File(this.baseDir + source.getRelativeSrcPath());1025if (!source.isSimpleCopy()) {1026if (srcPath.exists()) {1027Builder builder = new Builder();1028builder.setConfiguration(this);1029builder.setIncremental(incremental);1030builder.setMetadata(metadata);1031builder.setOptions(source.getOptions());10321033for (BuilderExtension extension : extensions) {1034builder.addExtension(extension);1035}10361037if (logger != null) {1038builder.setLogger(logger);1039} else if (noWarn) {1040builder.setLogger(new CommandlineNowarnLogger());1041} else {1042builder.setLogger(new CommandlineLogger());1043}10441045// Allow the builder to be aware of other sources (if there are any).1046// In the current setup the builder only preprocesses one source at a time1047// so it's not aware of other sources. This becomes an issue when external1048// messages must be written out. This allows the ExternalMessagesExtension1049// to check if it should load the messages already in the output file.1050if (++multipleSources > 1) {1051builder.setMultipleSources(true);1052}10531054builder.setSourceDir(srcPath);1055result &= builder.build();1056} else {1057System.out.println("Potential source : " + srcPath.toString() + " does not exist");1058result = false;1059}1060} else {1061String simpleOutput = source.getSimpleCopyOutput();10621063SimpleCopy simpleCopy = new SimpleCopy();1064simpleCopy.setBaseDir(baseDir);1065simpleCopy.setSourcePath(srcPath);1066simpleCopy.setSimpleOutput(simpleOutput);1067simpleCopy.setOutputDir(this.getOutputDir());1068result &= simpleCopy.copy();1069}1070}1071return result;1072}10731074/* [PR 121491] Use correct build method and correct commandline input values for preprocessing jobs */1075public boolean buildTests(boolean incremental, boolean noWarn, BuilderExtension[] extensions, Logger logger) {1076return buildTests(incremental, noWarn, extensions, logger, false);1077}10781079/**1080* Preprocesses this configuration object's automated tests.1081*1082* @param incremental <code>true</code> if this build is incremental1083* @param noWarn <code>true</code> if a logger with no warnings is to be used1084* @param extensions builder extensions to be added to the preprocess1085* @param logger the build logger to use, if <code>null</code> one will be created1086* @return <code>true</code> if the classes.txt is updated, <code>false</code> otherwise.1087*/1088public boolean buildTests(boolean incremental, boolean noWarn, BuilderExtension[] extensions, Logger logger, boolean callFromEclipse) {1089boolean result = true;1090int multipleSources = 0;1091List<Src> testsSources = this.registry.getTestsSources();1092String sourceRoot = this.registry.getSourceRoot();1093Builder builder = new Builder();1094builder.setConfiguration(this);1095builder.setIncremental(incremental);1096builder.setMetadata(metadata);1097/* [PR 116982] Preprocessor should not put INCLUDE-IF warnings on Automated Tests */1098if (this.registry.getConfigVersion() < 3) {1099builder.setNoWarnIncludeIf(true);1100builder.setIncludeIfUnsure(true);1101}1102for (BuilderExtension extension : extensions) {1103builder.addExtension(extension);1104}11051106if (logger != null) {1107builder.setLogger(logger);1108} else if (noWarn) {1109builder.setLogger(new CommandlineNowarnLogger());1110} else {1111builder.setLogger(new CommandlineLogger());1112}1113if (callFromEclipse) {1114if (this.registry.getConfigVersion() < 3) {1115builder.setOutputDir(new File(testsTempOutputPath));1116} else {1117builder.setOutputDir(new File(testsOutputPath));1118}1119}11201121/* Process explicit source paths for automated tests */1122if (testsSources != null) {1123for (int i = 0; i < testsSources.size(); i++) {1124File testsSrc = null;1125String srcfolder = testsSources.get(i).getRelativeSrcPath();1126if (callFromEclipse) {1127testsSrc = new File(this.baseDir + this.registry.getTestsProject(), srcfolder);1128} else {1129testsSrc = new File(this.baseDir + sourceRoot + srcfolder);1130}1131if (testsSrc.exists()) {1132// Allow the builder to be aware of other sources (if there are any).1133// // In the current setup the builder only preprocesses one source at a time1134// // so it's not aware of other sources. This becomes an issue when external1135// // messages must be written out. This allows the ExternalMessagesExtension1136// // to check if it should load the messages already in the output file.1137if (++multipleSources > 1) {1138builder.setMultipleSources(true);1139}1140builder.setSourceDir(testsSrc);1141result &= builder.build();1142} else {1143// this is just a warning, and does not indicate a failure1144System.out.println("Warning: Tests source directory does not exist: " + testsSrc.getAbsolutePath());1145}1146}1147}11481149/* [PR 119039] Design 917: Multiple source folder support for automated tests */1150for (Src source : sources) {1151if (!source.isSimpleCopy()) {1152String srcfolder = source.getRelativeSrcPath();1153if (srcfolder.startsWith(sourceRoot)) {1154/* [PR 121491] Use correct build method and correct commandline input values for preprocessing jobs */1155File testsSrc = null;1156if (callFromEclipse) {1157srcfolder = srcfolder.substring(sourceRoot.length());1158testsSrc = new File(this.baseDir + this.registry.getTestsProject(), srcfolder);1159} else {1160testsSrc = new File(this.baseDir + source.getRelativeSrcPath());1161}1162if (testsSrc.exists()) {1163// Allow the builder to be aware of other sources (if there are any).1164// In the current setup the builder only preprocesses one source at a time1165// so it's not aware of other sources. This becomes an issue when external1166// messages must be written out. This allows the ExternalMessagesExtension1167// to check if it should load the messages already in the output file.1168if (++multipleSources > 1) {1169builder.setMultipleSources(true);1170}1171builder.setSourceDir(testsSrc);1172result &= builder.build();1173} else {1174// this is just a warning, and does not indicate a failure1175System.out.println("Warning: Tests source directory does not exist: " + testsSrc.getAbsolutePath());1176}1177}1178}1179}1180return result;1181}11821183/* [PR 117967] idea 491: Automatically create the jars required for test bootpath */1184public boolean buildTestBootpath(boolean incremental, boolean noWarn, boolean useTestBootpathJavaDoc, BuilderExtension[] extensions, Logger logger, boolean callFromEclipse) {1185boolean result = true;1186int multipleSources = 0;1187String sourceRoot = this.registry.getSourceRoot();1188/* [PR 119039] Design 917: Multiple source folder support for automated tests */1189for (int i = 0; i < sources.size(); i++) {1190if (!sources.get(i).isSimpleCopy()) {1191String srcfolder = sources.get(i).getRelativeSrcPath();1192if (srcfolder.startsWith(sourceRoot)) {1193/* [PR 121491] Use correct build method and correct commandline input values for preprocessing jobs */1194File testsSrc = null;1195if (callFromEclipse) {1196srcfolder = srcfolder.substring(sourceRoot.length());1197testsSrc = new File(this.baseDir + this.registry.getTestsProject() + File.separator + srcfolder);1198} else {1199testsSrc = new File(this.baseDir + sources.get(i).getRelativeSrcPath());1200}1201if (testsSrc.exists()) {1202Builder builder = new Builder();1203builder.setConfiguration(this);1204builder.setIncremental(incremental);1205builder.setMetadata(metadata);1206/* [PR 116982] Preprocessor should not put INCLUDE-IF warnings on Automated Tests */1207builder.setNoWarnIncludeIf(true);1208/* [PR 121584]Add option to jcl-builder to use testsBootPath JavaDoc */1209builder.setIncludeIfUnsure(useTestBootpathJavaDoc);1210builder.setIsTestsBoot(true);12111212for (BuilderExtension extension : extensions) {1213builder.addExtension(extension);1214}12151216if (logger != null) {1217builder.setLogger(logger);1218} else if (noWarn) {1219builder.setLogger(new CommandlineNowarnLogger());1220} else {1221builder.setLogger(new CommandlineLogger());1222}12231224// Allow the builder to be aware of other sources (if there are any).1225// In the current setup the builder only preprocesses one source at a time1226// so it's not aware of other sources. This becomes an issue when external1227// messages must be written out. This allows the ExternalMessagesExtension1228// to check if it should load the messages already in the output file.1229if (++multipleSources > 1) {1230builder.setMultipleSources(true);1231}12321233builder.setSourceDir(testsSrc);1234if (callFromEclipse) {1235builder.setOutputDir(new File(testsTempBootOutputPath));1236}1237result &= builder.build();1238} else {1239// this is just a warning, and does not indicate a failure1240System.out.println("Warning: Tests source directory does not exist: " + testsSrc.getAbsolutePath());1241}1242}1243}1244}1245return result;1246}12471248/**1249* Returns a string representation of the config object with build info.1250*1251* @return the string representation of the config object1252*1253* @see java.lang.Object#toString()1254*/1255@Override1256public String toString() {1257return this.toString(true);1258}12591260/**1261* Returns a string representation of the config object with build info.1262* Build info includes the sources for the configuration and the output1263* directory.1264*1265* @param hasBuildInfo display build information1266* @return the string representation of the config object1267*1268* @see java.lang.Object#toString()1269*/1270public String toString(boolean hasBuildInfo) {1271StringBuffer buffer = new StringBuffer("\nName: ");1272buffer.append(name);12731274if (isConfiguration) {1275buffer.append(" (Configuration)");1276buffer.append("\nFlags: ");1277buffer.append(flagSet.toString());1278buffer.append("\nOptions: ");1279buffer.append(getOptions().toString());1280if (hasBuildInfo) {1281buffer.append("\nSources: ");1282buffer.append(sources.toString());1283buffer.append("\nOutput: ");1284buffer.append(getRelativeOutputPath());1285}1286} else {1287buffer.append(" (Set)");1288buffer.append("\nFlags: ");1289buffer.append(flagSet.toString());1290}1291return buffer.toString();1292}12931294/**1295* Indicates whether or not this ConfigObject is equal to a given object. The only1296* criteria used for comparison is the config name.1297*1298* @return <code>true</code> if the config objects have the same name, <code>false</code> otherwise1299*1300* @see java.lang.Object#equals(java.lang.Object)1301*/1302@Override1303public boolean equals(Object obj) {1304if (obj instanceof ConfigObject) {1305ConfigObject tempConfig = (ConfigObject) obj;13061307if (name.equals(tempConfig.getName())) {1308if (registry == null || registry.equals(tempConfig.getRegistry())) {1309return true;1310}1311}1312}13131314return false;1315}13161317/**1318* @see java.lang.Object#hashCode()1319*/1320@Override1321public int hashCode() {1322if (registry == null) {1323return name.hashCode();1324}13251326return name.hashCode() + registry.hashCode();1327}13281329/* [PR 120359] New classpath entry is needed for configurations */1330public void setXMLParserError(String error) {1331configXMLParserError += error;1332}13331334public String getXMLParserError() {1335return configXMLParserError;1336}1337}133813391340