summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgjoranv <gjoranv@gmail.com>2017-08-17 13:48:18 +0200
committerGitHub <noreply@github.com>2017-08-17 13:48:18 +0200
commit52f25f90f06fc5adce9f8f3f092f7d2c3a78c289 (patch)
treedc0071c314bee0d66fb96db73df3e6185c7cece1
parent6b2caed697a92f6d7ddd96e363ae7f6dbfdf9639 (diff)
parent81af3fc3a4c50b72ef45c310efbfc4f915e263e1 (diff)
Merge pull request #3133 from vespa-engine/gjoranv/config-def-package
Gjoranv/config def package
-rw-r--r--config-class-plugin/src/main/java/com/yahoo/vespa/ConfigGenMojo.java5
-rw-r--r--config-lib/src/test/java/com/yahoo/config/codegen/NamespaceAndPackageTest.java33
-rw-r--r--config-lib/src/test/resources/configdefinitions/arraytypes.def11
-rw-r--r--config-lib/src/test/resources/configdefinitions/chains-test.def42
-rw-r--r--config-lib/src/test/resources/configdefinitions/datastructures.def11
-rw-r--r--config-lib/src/test/resources/configdefinitions/defaulttest.def8
-rw-r--r--config-lib/src/test/resources/configdefinitions/md5test.def24
-rw-r--r--[-rwxr-xr-x]config-lib/src/test/resources/configdefinitions/namespace-and-package.def (renamed from config-lib/src/test/resources/configdefinitions/string.def)7
-rw-r--r--config-lib/src/test/resources/configdefinitions/namespace.def3
-rw-r--r--config-lib/src/test/resources/configdefinitions/package.def (renamed from config-lib/src/test/resources/configdefinitions/specialtypes.def)6
-rw-r--r--config-lib/src/test/resources/configdefinitions/simpletypes.def11
-rw-r--r--config-lib/src/test/resources/configdefinitions/standard.def8
-rw-r--r--config-lib/src/test/resources/configdefinitions/structtypes.def21
-rw-r--r--config-lib/src/test/resources/configdefinitions/test-nodefs.def16
-rw-r--r--config-lib/src/test/resources/configdefinitions/test-nonstring.def9
-rw-r--r--config-lib/src/test/resources/configdefinitions/test-reference.def4
-rw-r--r--config-lib/src/test/resources/configdefinitions/testnamespace.def3
-rw-r--r--config-lib/src/test/resources/configdefinitions/unicode.def5
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/CNode.java24
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/DefParser.java28
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/MakeConfig.java20
-rw-r--r--configgen/src/main/scala/com/yahoo/config/codegen/JavaClassBuilder.scala13
-rw-r--r--configgen/src/test/java/com/yahoo/config/codegen/DefParserNamespaceTest.java79
-rw-r--r--configgen/src/test/java/com/yahoo/config/codegen/DefParserPackageTest.java115
-rw-r--r--configgen/src/test/java/com/yahoo/config/codegen/DefParserTest.java217
25 files changed, 306 insertions, 417 deletions
diff --git a/config-class-plugin/src/main/java/com/yahoo/vespa/ConfigGenMojo.java b/config-class-plugin/src/main/java/com/yahoo/vespa/ConfigGenMojo.java
index 66c62b3b194..64941fdc1a2 100644
--- a/config-class-plugin/src/main/java/com/yahoo/vespa/ConfigGenMojo.java
+++ b/config-class-plugin/src/main/java/com/yahoo/vespa/ConfigGenMojo.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa;
+import com.yahoo.config.codegen.DefParser;
import com.yahoo.config.codegen.MakeConfig;
import com.yahoo.config.codegen.MakeConfigProperties;
import com.yahoo.config.codegen.PropertyException;
@@ -19,6 +20,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import static com.yahoo.config.codegen.DefParser.DEFAULT_PACKAGE_PREFIX;
+
/**
* Goal which generates config classes from def-files.
@@ -58,7 +61,7 @@ public class ConfigGenMojo extends AbstractMojo {
/**
* Package prefix of generated configs. The resulting package name will be packagePrefix.namespace if specified.
*/
- @Parameter(property = "plugin.configuration.packagePrefix", defaultValue = "com.yahoo.")
+ @Parameter(property = "plugin.configuration.packagePrefix", defaultValue = DEFAULT_PACKAGE_PREFIX)
private String packagePrefix;
/**
diff --git a/config-lib/src/test/java/com/yahoo/config/codegen/NamespaceAndPackageTest.java b/config-lib/src/test/java/com/yahoo/config/codegen/NamespaceAndPackageTest.java
new file mode 100644
index 00000000000..904d1fc049a
--- /dev/null
+++ b/config-lib/src/test/java/com/yahoo/config/codegen/NamespaceAndPackageTest.java
@@ -0,0 +1,33 @@
+package com.yahoo.config.codegen;
+
+import com.github.myproject.NamespaceAndPackageConfig;
+import com.github.myproject.PackageConfig;
+import com.yahoo.my.namespace.NamespaceConfig;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author gjoranv
+ */
+public class NamespaceAndPackageTest {
+ private static String NAMESPACE = "my.namespace";
+ private static String PACKAGE = "com.github.myproject";
+
+ @Test
+ public void namespace_is_set_from_def_file() {
+ assertThat(NamespaceConfig.CONFIG_DEF_NAMESPACE, is(NAMESPACE));
+ }
+
+ @Test
+ public void package_is_used_as_namespace_when_namespace_is_not_set_explicitly() {
+ assertThat(PackageConfig.CONFIG_DEF_NAMESPACE, is(PACKAGE));
+ }
+
+ @Test
+ public void package_does_not_override_namespace() {
+ assertThat(NamespaceAndPackageConfig.CONFIG_DEF_NAMESPACE, is(NAMESPACE));
+
+ }
+}
diff --git a/config-lib/src/test/resources/configdefinitions/arraytypes.def b/config-lib/src/test/resources/configdefinitions/arraytypes.def
deleted file mode 100644
index 8add790c6cb..00000000000
--- a/config-lib/src/test/resources/configdefinitions/arraytypes.def
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-# Config containing only simple array types that can be used for testing
-# individual types in detail.
-namespace=test
-
-boolarr[] bool
-doublearr[] double
-enumarr[] enum { VAL1, VAL2 }
-intarr[] int
-longarr[] long
-stringarr[] string
diff --git a/config-lib/src/test/resources/configdefinitions/chains-test.def b/config-lib/src/test/resources/configdefinitions/chains-test.def
deleted file mode 100644
index 88921b96d8d..00000000000
--- a/config-lib/src/test/resources/configdefinitions/chains-test.def
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-# Chains configuration
-namespace=test
-
-component[].id string
-
-# Configured functionality provided by this - comes in addition to those set in the code
-component[].dependencies.provides[] string
-
-# Configured "before" dependencies provided by this - comes in addition to those set in the code
-component[].dependencies.before[] string
-
-# Configured "after" dependencies provided by this - comes in addition to those set in the code
-component[].dependencies.after[] string
-
-# The id of this chain. The id has the form name(:version)?
-# where the version has the form 1(.2(.3(.identifier)?)?)?.
-# The default chain must be called "default".
-chain[].id string
-
-#The type of this chain
-chain[].type enum {DOCPROC, SEARCH} default=SEARCH
-
-# The id of a component to include in this chain.
-# The id has the form fullclassname(:version)?
-# where the version has the form 1(.2(.3(.identifier)?)?)?.
-chain[].component[] string
-
-# The optional list of chain ids this inherits.
-# The ids has the form name(:version)?
-# where the version has the form 1(.2(.3(.identifier)?)?)?.
-# If the version is not specified the newest version is used.
-chain[].inherit[] string
-
-# The optional list of component ids to exclude from this chain even if they exists in inherited chains
-# If versions are specified in these ids, they are ignored.
-chain[].exclude[] string
-
-# The phases for a chain
-chain[].phase[].id string
-chain[].phase[].before[] string
-chain[].phase[].after[] string
diff --git a/config-lib/src/test/resources/configdefinitions/datastructures.def b/config-lib/src/test/resources/configdefinitions/datastructures.def
deleted file mode 100644
index 7b0aa22c941..00000000000
--- a/config-lib/src/test/resources/configdefinitions/datastructures.def
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=test
-
-date[] string
-
-stock[].ticker string
-stock[].type enum { COMMON, ETF, ETC } default=COMMON
-stock[].volume[] int
-
-basicstruct.foo string default="foo"
-basicstruct.bar int default=0
diff --git a/config-lib/src/test/resources/configdefinitions/defaulttest.def b/config-lib/src/test/resources/configdefinitions/defaulttest.def
deleted file mode 100644
index d1210890184..00000000000
--- a/config-lib/src/test/resources/configdefinitions/defaulttest.def
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=test
-
-nondefaultstring string
-defaultstring string default="thedefault"
-
-nondefaultreference reference
-defaultreference reference default="thedefault"
diff --git a/config-lib/src/test/resources/configdefinitions/md5test.def b/config-lib/src/test/resources/configdefinitions/md5test.def
deleted file mode 100644
index b419bd975bb..00000000000
--- a/config-lib/src/test/resources/configdefinitions/md5test.def
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-# version=4 , version in comment does not count.
-
-# Added empty line to see if we can confuse
-# the server's md5 calculation
-namespace=test
-
-#even adding a variable name starting with 'version'
-versiontag int default=3
-
-blabla string default=""
-tabs string default=" "
-test int
-
-# test multiple spaces/tabs
-spaces int
-singletab string
-multitabs double
-
-# test enum
-normal enum { VAL1, VAL2 } default=VAL1
-spacevalues enum { V1 , V2 , V3 , V4 } default=V3
-
-# Comments and empty lines at the end
diff --git a/config-lib/src/test/resources/configdefinitions/string.def b/config-lib/src/test/resources/configdefinitions/namespace-and-package.def
index 63d189c3a4e..4e61d25f05f 100755..100644
--- a/config-lib/src/test/resources/configdefinitions/string.def
+++ b/config-lib/src/test/resources/configdefinitions/namespace-and-package.def
@@ -1,4 +1,7 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=test
-stringVal string default="_default_"
+namespace=my.namespace
+
+package=com.github.myproject
+
+a int
diff --git a/config-lib/src/test/resources/configdefinitions/namespace.def b/config-lib/src/test/resources/configdefinitions/namespace.def
index d92dc656fe7..b1406fc1bc4 100644
--- a/config-lib/src/test/resources/configdefinitions/namespace.def
+++ b/config-lib/src/test/resources/configdefinitions/namespace.def
@@ -1,6 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=test
-namespace=myproject.config
+namespace=my.namespace
a int
diff --git a/config-lib/src/test/resources/configdefinitions/specialtypes.def b/config-lib/src/test/resources/configdefinitions/package.def
index 3b129c810c0..eba915738a5 100644
--- a/config-lib/src/test/resources/configdefinitions/specialtypes.def
+++ b/config-lib/src/test/resources/configdefinitions/package.def
@@ -1,5 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=test
-myfile file
-myref reference
+package=com.github.myproject
+
+a int
diff --git a/config-lib/src/test/resources/configdefinitions/simpletypes.def b/config-lib/src/test/resources/configdefinitions/simpletypes.def
deleted file mode 100644
index b54ea43435d..00000000000
--- a/config-lib/src/test/resources/configdefinitions/simpletypes.def
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-# Config containing only simple leaf types with default values, that can be used
-# for testing individual types in detail.
-namespace=test
-
-boolval bool default=false
-doubleval double default=0.0
-enumval enum { VAL1, VAL2 } default=VAL1
-intval int default=0
-longval long default=0
-stringval string default="s"
diff --git a/config-lib/src/test/resources/configdefinitions/standard.def b/config-lib/src/test/resources/configdefinitions/standard.def
deleted file mode 100644
index 36c4c55f339..00000000000
--- a/config-lib/src/test/resources/configdefinitions/standard.def
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-# Config containing only simple leaf types with default values, that can be used
-# for testing individual types in detail.
-namespace=test
-
-basicStruct.intVal int default=0
-basicStruct.stringVal string default="s"
-stringArr[] string
diff --git a/config-lib/src/test/resources/configdefinitions/structtypes.def b/config-lib/src/test/resources/configdefinitions/structtypes.def
deleted file mode 100644
index 5a646efc2c7..00000000000
--- a/config-lib/src/test/resources/configdefinitions/structtypes.def
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-# Config containing only structs in various forms
-namespace=test
-
-simple.name string default="_default_"
-simple.gender enum { MALE, FEMALE } default=MALE
-simple.emails[] string
-
-nested.inner.name string default="_default_"
-nested.inner.gender enum { MALE, FEMALE } default=MALE
-nested.inner.emails[] string
-
-simplearr[].name string
-simplearr[].gender enum { MALE, FEMALE }
-
-nestedarr[].inner.name string
-nestedarr[].inner.gender enum { MALE, FEMALE }
-nestedarr[].inner.emails[] string
-
-complexarr[].innerarr[].name string
-complexarr[].innerarr[].gender enum { MALE, FEMALE }
diff --git a/config-lib/src/test/resources/configdefinitions/test-nodefs.def b/config-lib/src/test/resources/configdefinitions/test-nodefs.def
deleted file mode 100644
index b4c46a89695..00000000000
--- a/config-lib/src/test/resources/configdefinitions/test-nodefs.def
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=test
-
-# test config vars with no defaults
-
-s string
-j int
-b bool
-f double
-e enum { AA, BB, CC }
-
-basicstruct.foo string
-basicstruct.bar int
-
-arr[].s string
-arr[].i int
diff --git a/config-lib/src/test/resources/configdefinitions/test-nonstring.def b/config-lib/src/test/resources/configdefinitions/test-nonstring.def
deleted file mode 100644
index 474c11770f1..00000000000
--- a/config-lib/src/test/resources/configdefinitions/test-nonstring.def
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=test
-
-# Test non-string config vars with defaults
-
-i int default=0
-b bool default=false
-d double default=0.0
-e enum { AA, BB, CC } default=AA
diff --git a/config-lib/src/test/resources/configdefinitions/test-reference.def b/config-lib/src/test/resources/configdefinitions/test-reference.def
deleted file mode 100644
index b5d82a7b4b6..00000000000
--- a/config-lib/src/test/resources/configdefinitions/test-reference.def
+++ /dev/null
@@ -1,4 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=test
-
-configId reference default=":parent:"
diff --git a/config-lib/src/test/resources/configdefinitions/testnamespace.def b/config-lib/src/test/resources/configdefinitions/testnamespace.def
deleted file mode 100644
index 6e58c691097..00000000000
--- a/config-lib/src/test/resources/configdefinitions/testnamespace.def
+++ /dev/null
@@ -1,3 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=foo
-basicStruct.stringVal string
diff --git a/config-lib/src/test/resources/configdefinitions/unicode.def b/config-lib/src/test/resources/configdefinitions/unicode.def
deleted file mode 100644
index 9a1ab170d92..00000000000
--- a/config-lib/src/test/resources/configdefinitions/unicode.def
+++ /dev/null
@@ -1,5 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=test
-
-unicodestring1 string
-unicodestring2 string default="abc æøå 囲碁 ÆØÅ ABC"
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/CNode.java b/configgen/src/main/java/com/yahoo/config/codegen/CNode.java
index 260d70ad0e6..9dc7923b71b 100644
--- a/configgen/src/main/java/com/yahoo/config/codegen/CNode.java
+++ b/configgen/src/main/java/com/yahoo/config/codegen/CNode.java
@@ -3,10 +3,12 @@ package com.yahoo.config.codegen;
import java.util.StringTokenizer;
+import static com.yahoo.config.codegen.DefParser.DEFAULT_PACKAGE_PREFIX;
+
/**
* Abstract superclass for all nodes representing a definition file.
*
- * @author <a href="gv@yahoo-inc.com">G. Voldengen</a>
+ * @author gjoranv
*/
public abstract class CNode {
@@ -20,8 +22,10 @@ public abstract class CNode {
// TODO: remove! Only set for the root node, and root.getName() returns the same thing!
String defName = null;
+
String defVersion = "";
String defNamespace = null;
+ String defPackage = null;
String defMd5 = "MISSING MD5";
String comment = "";
@@ -78,13 +82,21 @@ public abstract class CNode {
}
public String getNamespace() {
- return defNamespace;
+ if (defNamespace != null) return defNamespace;
+ if (defPackage != null) return defPackage;
+ return null;
}
void setNamespace(String namespace) {
defNamespace = namespace;
}
+ public String getPackage() {
+ return defPackage;
+ }
+
+ void setPackage(String defPackage) { this.defPackage = defPackage; }
+
public String getComment() {
return comment;
}
@@ -155,7 +167,11 @@ public abstract class CNode {
@Override
public String toString() {
- return getNamespace()+"."+getName()+","+getVersion();
+ return "CNode{" +
+ "namespace='" + defNamespace + '\'' +
+ ", package='" + defPackage + '\'' +
+ ", name='" + name + '\'' +
+ ", version='" + defVersion + '\'' +
+ '}';
}
-
}
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/DefParser.java b/configgen/src/main/java/com/yahoo/config/codegen/DefParser.java
index 54b882d276c..09a751bdde7 100644
--- a/configgen/src/main/java/com/yahoo/config/codegen/DefParser.java
+++ b/configgen/src/main/java/com/yahoo/config/codegen/DefParser.java
@@ -14,10 +14,17 @@ import java.util.regex.Pattern;
* @author hmusum
*/
public class DefParser {
+ public static final String DEFAULT_PACKAGE_PREFIX = "com.yahoo.";
+
static final Pattern commentPattern = Pattern.compile("^\\s*#+\\s*(.*?)\\s*$");
public static final Pattern versionPattern = Pattern.compile("^(version\\s*=\\s*)([0-9][0-9-]*)$");
- // Namespace must start with a letter, since Java (Java language Spec, section 3.8) and C++ identifiers cannot start with a digit
- public static final Pattern namespacePattern = Pattern.compile("^(namespace\\s*=\\s*)(([a-z][a-z0-9_]*)+([.][a-z][a-z0-9_]*)*)$");
+ // Namespace/package must start with a letter, since Java (Java language Spec, section 3.8) and C++ identifiers cannot start with a digit
+ public static final Pattern namespacePattern = getNamespacePattern("namespace");
+ public static final Pattern packagePattern = getNamespacePattern("package");
+
+ private static Pattern getNamespacePattern(String directive) {
+ return Pattern.compile("^(" + directive + "\\s*=\\s*)(([a-z][a-z0-9_]*)+([.][a-z][a-z0-9_]*)*)$");
+ }
private final BufferedReader reader;
private final String name;
@@ -127,6 +134,12 @@ public class DefParser {
nd.addNormalizedLine(line);
return;
}
+ Matcher packageMatcher = packagePattern.matcher(line);
+ if (packageMatcher.matches()) {
+ parsePackageLine(packageMatcher.group(2));
+ nd.addNormalizedLine(line);
+ return;
+ }
// Only add lines that are not version, namespace or comment lines
nd.addNormalizedLine(line);
DefLine defLine = new DefLine(line);
@@ -148,14 +161,19 @@ public class DefParser {
}
private void parseNamespaceLine(String namespace) {
- if (namespace.startsWith("com.yahoo."))
- throw new IllegalArgumentException("Remove 'com.yahoo.' from the namespace '" + namespace +
- "' - it will be automatically added to the java package name.");
+ if (namespace.startsWith(DEFAULT_PACKAGE_PREFIX))
+ throw new IllegalArgumentException("Please use 'package' instead of 'namespace'.");
root.setNamespace(namespace);
root.setComment(comment);
comment = "";
}
+ private void parsePackageLine(String defPackage) {
+ root.setPackage(defPackage);
+ root.setComment(comment);
+ comment = "";
+ }
+
void parseLines(CNode root, List<String> defLines, NormalizedDefinition nd) throws DefParserException {
DefParserException failure = null;
int lineNumber = 1;
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/MakeConfig.java b/configgen/src/main/java/com/yahoo/config/codegen/MakeConfig.java
index 91d401c91b7..ef9af1c2b11 100644
--- a/configgen/src/main/java/com/yahoo/config/codegen/MakeConfig.java
+++ b/configgen/src/main/java/com/yahoo/config/codegen/MakeConfig.java
@@ -18,11 +18,11 @@ public class MakeConfig {
classBuilder = createClassBuilder(root, nd, properties);
}
- public static ClassBuilder createClassBuilder(InnerCNode root, NormalizedDefinition nd, MakeConfigProperties prop) {
- if (prop.language.equals("cppng") || prop.language.equals("cpp"))
- return new CppClassBuilder(root, nd, prop.destDir, prop.dirInRoot);
+ public static ClassBuilder createClassBuilder(InnerCNode root, NormalizedDefinition nd, MakeConfigProperties properties) {
+ if (isCpp(properties))
+ return new CppClassBuilder(root, nd, properties.destDir, properties.dirInRoot);
else
- return new JavaClassBuilder(root, nd, prop.destDir, prop.javaPackagePrefix);
+ return new JavaClassBuilder(root, nd, properties.destDir, properties.javaPackagePrefix);
}
public static boolean makeConfig(MakeConfigProperties properties) throws FileNotFoundException {
@@ -31,7 +31,7 @@ public class MakeConfig {
if (name.endsWith(".def")) name = name.substring(0, name.length() - 4);
DefParser parser = new DefParser(name, new FileReader(specFile));
InnerCNode configRoot = parser.getTree();
- checkNamespace(name, configRoot);
+ checkNamespaceAndPacakge(name, configRoot, isCpp(properties));
if (configRoot != null) {
MakeConfig mc = new MakeConfig(configRoot, parser.getNormalizedDefinition(), properties);
mc.buildClasses();
@@ -73,9 +73,15 @@ public class MakeConfig {
}
}
- private static void checkNamespace(String name, InnerCNode configRoot) {
- if (configRoot.defNamespace == null)
+ private static void checkNamespaceAndPacakge(String name, InnerCNode configRoot, boolean isCpp) {
+ if (isCpp && configRoot.defNamespace == null)
throw new IllegalArgumentException("In config definition '" + name + "': A namespace is required");
+ if (configRoot.defNamespace == null && configRoot.defPackage == null)
+ throw new IllegalArgumentException("In config definition '" + name + "': A package (or namespace) is required");
+ }
+
+ private static boolean isCpp(MakeConfigProperties properties) {
+ return (properties.language.equals("cppng") || properties.language.equals("cpp"));
}
// The Exceptions class below is copied from vespajlib/com.yahoo.protect.Exceptions
diff --git a/configgen/src/main/scala/com/yahoo/config/codegen/JavaClassBuilder.scala b/configgen/src/main/scala/com/yahoo/config/codegen/JavaClassBuilder.scala
index 578a8215750..518815d5a10 100644
--- a/configgen/src/main/scala/com/yahoo/config/codegen/JavaClassBuilder.scala
+++ b/configgen/src/main/scala/com/yahoo/config/codegen/JavaClassBuilder.scala
@@ -4,6 +4,7 @@ package com.yahoo.config.codegen
import java.io.{File, FileNotFoundException, FileOutputStream, PrintStream}
import com.yahoo.config.codegen.ConfigGenerator.{createClassName, indentCode}
+import com.yahoo.config.codegen.DefParser.DEFAULT_PACKAGE_PREFIX
import scala.collection.JavaConverters._
import scala.util.Random
@@ -22,13 +23,13 @@ class JavaClassBuilder(
{
import JavaClassBuilder._
- val packagePrefix = if (rawPackagePrefix != null) rawPackagePrefix else "com.yahoo."
- val javaPackage = packagePrefix + root.getNamespace
+ val packagePrefix = if (rawPackagePrefix != null) rawPackagePrefix else DEFAULT_PACKAGE_PREFIX
+ val javaPackage = if (root.getPackage != null) root.getPackage else packagePrefix + root.getNamespace
val className = createClassName(root.getName)
override def createConfigClasses() {
try {
- val outFile = new File(getDestPath(destDir, root.getNamespace), className + ".java")
+ val outFile = new File(getDestPath(destDir, javaPackage), className + ".java")
var out: PrintStream = null
try {
out = new PrintStream(new FileOutputStream(outFile))
@@ -126,12 +127,12 @@ class JavaClassBuilder(
/**
* @param rootDir The root directory for the destination path.
- * @param namespace The namespace from the def file
+ * @param javaPackage The java package
* @return the destination path for the generated config file, including the given rootDir.
*/
- private def getDestPath(rootDir: File, namespace: String): File = {
+ private def getDestPath(rootDir: File, javaPackage: String): File = {
var dir: File = rootDir
- val subDirs: Array[String] = (packagePrefix + namespace).split("""\.""")
+ val subDirs: Array[String] = javaPackage.split("""\.""")
for (subDir <- subDirs) {
dir = new File(dir, subDir)
this.synchronized {
diff --git a/configgen/src/test/java/com/yahoo/config/codegen/DefParserNamespaceTest.java b/configgen/src/test/java/com/yahoo/config/codegen/DefParserNamespaceTest.java
index a0dff8dcf91..049ed3fe753 100644
--- a/configgen/src/test/java/com/yahoo/config/codegen/DefParserNamespaceTest.java
+++ b/configgen/src/test/java/com/yahoo/config/codegen/DefParserNamespaceTest.java
@@ -4,13 +4,12 @@ import org.junit.Test;
import java.io.IOException;
-import static com.yahoo.config.codegen.DefParserTest.assertExceptionAndMessage;
+import static com.yahoo.config.codegen.DefParserTest.assertLineFails;
import static com.yahoo.config.codegen.DefParserTest.createDefTemplate;
import static com.yahoo.config.codegen.DefParserTest.createParser;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
/**
* @author gjoranv
@@ -20,66 +19,53 @@ public class DefParserNamespaceTest {
@Test
public void namespace_is_set_on_root_node() {
- DefParser parser = createParser("version=1\nnamespace=myproject.config\na string\n");
+ DefParser parser = createParser("version=1\nnamespace=myproject.config\n");
CNode root = parser.getTree();
assertThat(root.getNamespace(), is("myproject.config"));
}
+ @Test
+ public void package_is_used_as_namespace_when_no_namespace_is_given() {
+ String PACKAGE = "com.github.myproject";
+ DefParser parser = createParser("package=" + PACKAGE + "\n");
+ CNode root = parser.getTree();
+ assertThat(root.getNamespace(), is(PACKAGE));
+ }
+
@Test(expected = CodegenRuntimeException.class)
public void uppercase_chars_are_not_allowed() {
- createParser("version=1\nnamespace=Foo\na string\n").getTree();
+ createParser("version=1\nnamespace=Foo\n").getTree();
}
@Test(expected = CodegenRuntimeException.class)
public void explicit_com_yahoo_prefix_is_not_allowed() {
createParser("version=1\n" +
- "namespace=com.yahoo.myproject.config\n" +
- "a string\n").getTree();
+ "namespace=com.yahoo.myproject.config\n").getTree();
}
@Test
public void spaces_are_allowed_around_equals_sign() {
- DefParser parser = createParser("version=1\nnamespace = myproject.config\na string\n");
+ DefParser parser = createParser("version=1\nnamespace = myproject.config\n");
CNode root = parser.getTree();
assertThat(root.getNamespace(), is("myproject.config"));
}
@Test
public void empty_namespace_is_not_allowed() {
- // invalid
- DefParser parser = createParser("version=1\nnamespace \na string\n");
- try {
- parser.getTree();
- fail();
- } catch (Exception e) {
- //e.printStackTrace();
- assertExceptionAndMessage(e, CodegenRuntimeException.class,
- "Error parsing or reading config definition.Error when parsing line 2: namespace \n" +
- "namespace");
- }
+ assertLineFails("namespace");
}
@Test
public void consecutive_dots_are_not_allowed() {
- // invalid
- DefParser parser = createParser("version=1\nnamespace=a..b\na string\n");
- try {
- parser.getTree();
- fail();
- } catch (Exception e) {
- //e.printStackTrace();
- assertExceptionAndMessage(e, CodegenRuntimeException.class,
- "Error parsing or reading config definition.Error when parsing line 2: namespace=a..b\n" +
- "namespace=a..b");
- }
+ assertLineFails("namespace=a..b");
}
@Test
public void namespace_alters_def_md5() {
- DefParser parser = createParser("version=1\na string\n");
+ DefParser parser = createParser("version=1\n");
CNode root = parser.getTree();
- parser = createParser("version=1\nnamespace=myproject.config\na string\n");
+ parser = createParser("version=1\nnamespace=myproject.config\n");
CNode namespaceRoot = parser.getTree();
assertThat(root.defMd5, not(namespaceRoot.defMd5));
@@ -89,51 +75,30 @@ public class DefParserNamespaceTest {
@Test
public void number_is_allowed_as_non_leading_char_in_namespace() throws IOException, DefParser.DefParserException {
StringBuilder sb = createDefTemplate();
- String line = "namespace=a.b.c2\nfoo int\n";
+ String line = "namespace=a.b.c2\n";
sb.append(line);
createParser(sb.toString()).parse();
}
@Test
public void number_is_not_allowed_as_namespace_start_char() throws IOException, DefParser.DefParserException {
- StringBuilder sb = createDefTemplate();
- String line = "namespace=2.a.b";
- sb.append(line).append("\n");
- Class<?> exceptionClass = DefParser.DefParserException.class;
- try {
- createParser(sb.toString()).parse();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + line + "\n" + line);
- }
+ assertLineFails("namespace=2.a.b");
}
@Test
public void number_is_not_allowed_as_leading_char_in_namespace_token() throws IOException, DefParser.DefParserException {
- StringBuilder sb = createDefTemplate();
- String line = "namespace=a.b.2c";
- sb.append(line).append("\n");
- Class<?> exceptionClass = DefParser.DefParserException.class;
- try {
- createParser(sb.toString()).parse();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + line + "\n" + line);
- }
-
+ assertLineFails("namespace=a.b.2c");
}
@Test
public void underscore_in_namespace_is_allowed() throws IOException, DefParser.DefParserException {
StringBuilder sb = createDefTemplate();
- String line = "namespace=a_b.c\nfoo int\n";
+ String line = "namespace=a_b.c\n";
sb.append(line);
createParser(sb.toString()).parse();
sb = createDefTemplate();
- line = "namespace=a_b.c_d\nfoo int\n";
+ line = "namespace=a_b.c_d\n";
sb.append(line);
createParser(sb.toString()).parse();
}
diff --git a/configgen/src/test/java/com/yahoo/config/codegen/DefParserPackageTest.java b/configgen/src/test/java/com/yahoo/config/codegen/DefParserPackageTest.java
new file mode 100644
index 00000000000..97986a05bf3
--- /dev/null
+++ b/configgen/src/test/java/com/yahoo/config/codegen/DefParserPackageTest.java
@@ -0,0 +1,115 @@
+package com.yahoo.config.codegen;
+
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static com.yahoo.config.codegen.DefParser.DEFAULT_PACKAGE_PREFIX;
+import static com.yahoo.config.codegen.DefParserTest.assertLineFails;
+import static com.yahoo.config.codegen.DefParserTest.createDefTemplate;
+import static com.yahoo.config.codegen.DefParserTest.createParser;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests setting explicit java package in the def file.
+ *
+ * @author gjoranv
+ */
+public class DefParserPackageTest {
+ String PACKAGE = "com.github.myproject";
+
+ @Test
+ public void package_is_set_on_root_node() {
+ DefParser parser = createParser("package=" + PACKAGE + "\n");
+ CNode root = parser.getTree();
+ assertThat(root.getPackage(), is(PACKAGE));
+ }
+
+ @Test
+ public void package_and_namespace_can_coexist() {
+ String namespace = "test.namespace";
+ DefParser parser = createParser("package=" + PACKAGE +
+ "\nnamespace=" + namespace +"\n");
+ CNode root = parser.getTree();
+ assertThat(root.getPackage(), is(PACKAGE));
+ assertThat(root.getNamespace(), is(namespace));
+ }
+
+ // Required by JavaClassBuilder ctor.
+ @Test
+ public void package_is_null_when_not_explicitly_given() {
+ String namespace = "test.namespace";
+ DefParser parser = createParser("namespace=" + namespace + "\n");
+ CNode root = parser.getTree();
+ assertThat(root.getPackage(), nullValue());
+ }
+
+ @Test(expected = CodegenRuntimeException.class)
+ public void uppercase_chars_are_not_allowed() {
+ createParser("package=Foo.bar\n").getTree();
+ }
+
+ @Test
+ public void spaces_are_allowed_around_equals_sign() {
+ DefParser parser = createParser("package = " + PACKAGE + "\n");
+ CNode root = parser.getTree();
+ assertThat(root.getPackage(), is(PACKAGE));
+ }
+
+ @Test
+ public void empty_package_is_not_allowed() {
+ assertLineFails("package");
+ }
+
+ @Test
+ public void consecutive_dots_are_not_allowed() {
+ assertLineFails("package=a..b");
+ }
+
+ @Test
+ public void package_alters_def_md5() {
+ DefParser parser = createParser("a string\n");
+ CNode root = parser.getTree();
+
+ parser = createParser("package=" + PACKAGE + "\na string\n");
+ CNode rootWithPackage = parser.getTree();
+
+ assertThat(root.defMd5, not(rootWithPackage.defMd5));
+ }
+
+
+ @Test
+ public void number_is_allowed_as_non_leading_char() throws IOException, DefParser.DefParserException {
+ StringBuilder sb = createDefTemplate();
+ String line = "package=a.b.c2\n";
+ sb.append(line);
+ createParser(sb.toString()).parse();
+ }
+
+ @Test
+ public void number_is_not_allowed_as_package_start_char() throws IOException, DefParser.DefParserException {
+ assertLineFails("package=2.a.b");
+ }
+
+ @Test
+ public void number_is_not_allowed_as_leading_char_in_package_token() throws IOException, DefParser.DefParserException {
+ assertLineFails("package=a.b.2c");
+ }
+
+ @Test
+ public void underscore_in_package_is_allowed() throws IOException, DefParser.DefParserException {
+ StringBuilder sb = createDefTemplate();
+ String line = "package=a_b.c\n";
+ sb.append(line);
+ createParser(sb.toString()).parse();
+
+ sb = createDefTemplate();
+ line = "package=a_b.c_d\n";
+ sb.append(line);
+ createParser(sb.toString()).parse();
+ }
+
+}
diff --git a/configgen/src/test/java/com/yahoo/config/codegen/DefParserTest.java b/configgen/src/test/java/com/yahoo/config/codegen/DefParserTest.java
index a59a70751b4..e36641bb626 100644
--- a/configgen/src/test/java/com/yahoo/config/codegen/DefParserTest.java
+++ b/configgen/src/test/java/com/yahoo/config/codegen/DefParserTest.java
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.codegen;
-import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
@@ -83,16 +82,8 @@ public class DefParserTest {
@Test
public void testInvalidType() {
- Class<?> exceptionClass = DefParser.DefParserException.class;
- try {
- createParser("version=1\n" +
- "# comment\n" +
- "a sting").getTree();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage((Exception) e.getCause(), exceptionClass,
- "Error when parsing line 3: a sting", false);
- }
+ String line = "a sting";
+ assertLineFails(line, "Could not create sting a");
}
@Test
@@ -116,7 +107,7 @@ public class DefParserTest {
}
@Test
- public void testMissingVersion() {
+ public void version_is_not_mandatory() {
try {
createParser("a string\n").parse();
} catch (Exception e) {
@@ -148,31 +139,8 @@ public class DefParserTest {
@Test
public void verify_fail_on_default_for_file() {
- Class<?> exceptionClass = DefParser.DefParserException.class;
- DefParser parser = createParser("version=1\nf file default=\"file1.txt\"\n");
- try {
- parser.getTree();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage((Exception) e.getCause(), exceptionClass,
- "Error when parsing line 2: f file default=\"file1.txt\"\n" +
- "Invalid default value", false);
- }
- }
-
- // Helper method for checking correct exception class and message
- static void assertExceptionAndMessage(Exception e, Class<?> exceptionClass, String message) {
- assertExceptionAndMessage(e, exceptionClass, message, true);
- }
-
- // Helper method for checking correct exception class and message
- static void assertExceptionAndMessage(Exception e, Class<?> exceptionClass, String message, boolean exact) {
- if (exact) {
- assertEquals(message, e.getMessage());
- } else {
- assertTrue(e.getMessage().startsWith(message));
- }
- assertEquals(exceptionClass.getName(), e.getClass().getName());
+ assertLineFails("f file default=\"file1.txt\"",
+ "Invalid default value");
}
@Test(expected = CodegenRuntimeException.class)
@@ -230,180 +198,115 @@ public class DefParserTest {
}
@Test
- public void testInvalidLine() {
- Class<?> exceptionClass = DefParser.DefParserException.class;
- StringBuilder sb = createDefTemplate();
- String invalidLine = "a inta\n";
- sb.append(invalidLine);
- try {
- createParser(sb.toString()).parse();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + invalidLine + "Could not create inta a");
- }
- }
-
- @Test
- public void testDuplicateDefinition() {
+ public void duplicate_parameter_is_illegal() {
Class<?> exceptionClass = DefParser.DefParserException.class;
StringBuilder sb = createDefTemplate();
- String invalidLine = "b int\n";
- sb.append(invalidLine);
- // Add a duplicate line, which should be illegal
- sb.append(invalidLine);
+ String duplicateLine = "b int\n";
+ sb.append(duplicateLine);
+ sb.append(duplicateLine);
try {
createParser(sb.toString()).parse();
fail("Didn't find expected exception of type " + exceptionClass);
} catch (Exception e) {
assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 4: " + invalidLine + "b is already defined");
+ "Error when parsing line 4: " + duplicateLine + "b is already defined");
}
}
@Test
public void testIllegalCharacterInName() {
- Class<?> exceptionClass = DefParser.DefParserException.class;
- StringBuilder sb = createDefTemplate();
- String invalidLine = "a-b int\n";
- sb.append(invalidLine);
- try {
- createParser(sb.toString()).parse();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + invalidLine + "a-b contains unexpected character");
- }
+ assertLineFails("a-b int",
+ "a-b contains unexpected character");
}
@Test
public void parameter_name_starting_with_digit_is_illegal() {
- Class<?> exceptionClass = DefParser.DefParserException.class;
- StringBuilder sb = createDefTemplate();
- String invalidLine = "1a int\n";
- sb.append(invalidLine);
- try {
- createParser(sb.toString()).parse();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + invalidLine + "1a must start with a non-digit character");
- }
+ assertLineFails("1a int",
+ "1a must start with a non-digit character");
}
@Test
public void parameter_name_starting_with_uppercase_is_illegal() {
- Class<?> exceptionClass = DefParser.DefParserException.class;
- StringBuilder sb = createDefTemplate();
- String invalidLine = "SomeInt int\n";
- sb.append(invalidLine);
- try {
- createParser(sb.toString()).parse();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + invalidLine + "'SomeInt' cannot start with an uppercase letter");
- }
+ assertLineFails("SomeInt int",
+ "'SomeInt' cannot start with an uppercase letter");
}
@Test
public void parameter_name_starting_with_the_internal_prefix_is_illegal() {
String internalPrefix = ReservedWords.INTERNAL_PREFIX;
- Class<?> exceptionClass = DefParser.DefParserException.class;
- StringBuilder sb = createDefTemplate();
- String invalidLine = internalPrefix + "i int\n";
- sb.append(invalidLine);
- try {
- createParser(sb.toString()).parse();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + invalidLine +
- "'" + internalPrefix + "i' cannot start with '" + internalPrefix + "'");
- }
+ assertLineFails(internalPrefix + "i int",
+ "'" + internalPrefix + "i' cannot start with '" + internalPrefix + "'");
}
@Test
public void testIllegalArray() {
- Class<?> exceptionClass = DefParser.DefParserException.class;
- StringBuilder sb = createDefTemplate();
- String invalidLine = "intArr[ int\n";
- sb.append(invalidLine);
- try {
- createParser(sb.toString()).parse();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + invalidLine + "intArr[ Expected ] to terminate array definition");
- }
+ assertLineFails("intArr[ int",
+ "intArr[ Expected ] to terminate array definition");
}
@Test
public void testIllegalDefault() {
- Class<?> exceptionClass = DefParser.DefParserException.class;
- StringBuilder sb = createDefTemplate();
- String invalidLine = "a int deflt 10\n";
- sb.append(invalidLine);
- try {
- createParser(sb.toString()).parse();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + invalidLine + " deflt 10");
- }
+ assertLineFails("a int deflt 10",
+ " deflt 10");
}
@Test
public void testReservedWordInC() {
- Class<?> exceptionClass = DefParser.DefParserException.class;
- StringBuilder sb = createDefTemplate();
- String invalidLine = "auto int\n";
- sb.append(invalidLine);
- try {
- createParser(sb.toString()).parse();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + invalidLine + "auto is a reserved word in C");
- }
+ assertLineFails("auto int",
+ "auto is a reserved word in C");
}
@Test
public void testReservedWordInJava() {
- Class<?> exceptionClass = DefParser.DefParserException.class;
- StringBuilder sb = createDefTemplate();
- String invalidLine = "abstract int\n";
- sb.append(invalidLine);
- try {
- createParser(sb.toString()).parse();
- fail("Didn't find expected exception of type " + exceptionClass);
- } catch (Exception e) {
- assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + invalidLine + "abstract is a reserved word in Java");
- }
+ assertLineFails("abstract int",
+ "abstract is a reserved word in Java");
}
@Test
public void testReservedWordInCAndJava() {
- Class<?> exceptionClass = DefParser.DefParserException.class;
+ assertLineFails("continue int",
+ "continue is a reserved word in C and Java");
+ }
+
+ static StringBuilder createDefTemplate() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("version=8\n");
+ // Add a comment line to check that we get correct line number with comments
+ sb.append("# comment\n");
+
+ return sb;
+ }
+
+ static void assertLineFails(String line) {
+ assertLineFails(line, line);
+ }
+
+ static void assertLineFails(String line, String message) {
StringBuilder sb = createDefTemplate();
- String invalidLine = "continue int\n";
- sb.append(invalidLine);
+ sb.append(line).append("\n");
+ Class<?> exceptionClass = DefParser.DefParserException.class;
try {
createParser(sb.toString()).parse();
fail("Didn't find expected exception of type " + exceptionClass);
} catch (Exception e) {
assertExceptionAndMessage(e, exceptionClass,
- "Error when parsing line 3: " + invalidLine + "continue is a reserved word in C and Java");
+ "Error when parsing line 3: " + line + "\n" + message);
}
}
- static StringBuilder createDefTemplate() {
- StringBuilder sb = new StringBuilder();
- sb.append("version=8\n");
- // Add a comment line to check that we get correct line number with comments
- sb.append("# comment\n");
+ // Helper method for checking correct exception class and message
+ private static void assertExceptionAndMessage(Exception e, Class<?> exceptionClass, String message) {
+ assertExceptionAndMessage(e, exceptionClass, message, true);
+ }
- return sb;
+ // Helper method for checking correct exception class and message
+ private static void assertExceptionAndMessage(Exception e, Class<?> exceptionClass, String message, boolean exact) {
+ if (exact) {
+ assertEquals(message, e.getMessage());
+ } else {
+ assertTrue(e.getMessage().startsWith(message));
+ }
+ assertEquals(exceptionClass.getName(), e.getClass().getName());
}
+
}