diff --git a/.classpath b/.classpath index 1c53fa4a..390c2cb5 100644 --- a/.classpath +++ b/.classpath @@ -19,6 +19,9 @@ + + + diff --git a/build-cobertura.xml b/build-cobertura.xml new file mode 100755 index 00000000..875ea6b7 --- /dev/null +++ b/build-cobertura.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-common.xml b/build-common.xml new file mode 100755 index 00000000..bcf1568a --- /dev/null +++ b/build-common.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ! ! ! I M P O R T A N T ! ! ! + +This file is not designed to be invoked directly, however it is included and +invoked by each of the individual projects. + + + + + +type: + ant -p + +For a complete list of available targets + + + \ No newline at end of file diff --git a/build-javac.xml b/build-javac.xml new file mode 100755 index 00000000..496fc6c3 --- /dev/null +++ b/build-javac.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/build.xml b/build.xml index d389576b..c2efdecb 100644 --- a/build.xml +++ b/build.xml @@ -16,6 +16,9 @@ + + + diff --git a/dist/h2zero.jar b/dist/h2zero.jar index 399cf0dc..58b16bef 100644 Binary files a/dist/h2zero.jar and b/dist/h2zero.jar differ diff --git a/dist/h2zero.zip b/dist/h2zero.zip index 3dc5f6ee..3bfe88b2 100644 Binary files a/dist/h2zero.zip and b/dist/h2zero.zip differ diff --git a/lib/runtime/commons-validator-1.4.1.jar b/lib/runtime/commons-validator-1.4.1.jar new file mode 100644 index 00000000..95c951e0 Binary files /dev/null and b/lib/runtime/commons-validator-1.4.1.jar differ diff --git a/lib/runtime/sqlite-jdbc-3.8.11.1.jar b/lib/runtime/sqlite-jdbc-3.8.11.1.jar new file mode 100644 index 00000000..f1537e49 Binary files /dev/null and b/lib/runtime/sqlite-jdbc-3.8.11.1.jar differ diff --git a/lib/runtime/templar.jar b/lib/runtime/templar.jar index ec35b23c..0e9aa840 100644 Binary files a/lib/runtime/templar.jar and b/lib/runtime/templar.jar differ diff --git a/src/main/java/synapticloop/h2zero/H2ZeroParser.java b/src/main/java/synapticloop/h2zero/H2ZeroParser.java index ad3c6032..a82d9d53 100755 --- a/src/main/java/synapticloop/h2zero/H2ZeroParser.java +++ b/src/main/java/synapticloop/h2zero/H2ZeroParser.java @@ -157,6 +157,10 @@ public class H2ZeroParser { } } + /* + * The following is used to determine the max width of the validators, for + * logging out the information through the SimpleLogger + */ private static int maxValidatorClassNameLength = 0; static { for (BaseValidator validator : validators) { @@ -167,6 +171,12 @@ public class H2ZeroParser { } } + /** + * Pare a .h2zero file + * + * @param file The file to be parsed + * @throws H2ZeroParseException if there was an error parsing the file + */ public H2ZeroParser(File file) throws H2ZeroParseException { JSONObject jsonObject = null; try { diff --git a/src/main/java/synapticloop/h2zero/ant/H2ZeroTask.java b/src/main/java/synapticloop/h2zero/ant/H2ZeroTask.java index 55601dce..c712e246 100755 --- a/src/main/java/synapticloop/h2zero/ant/H2ZeroTask.java +++ b/src/main/java/synapticloop/h2zero/ant/H2ZeroTask.java @@ -253,16 +253,6 @@ private void logDatabaseInfo(H2ZeroParser h2zeroParser) { } } - /** - * - * @param in the h2zero file to use as the input - * @deprecated - */ - @Deprecated - public void setIn(String in) { - getProject().log("Attribute 'in' is depecrated and __MUST__ be replced with 'inFile', exitting...", Project.MSG_ERR); - System.exit(-1); - } public String getInFile() { return inFile; } public void setInFile(String inFile) { this.inFile = inFile; } public void setOutDir(String outDir) { this.outDir = outDir; } diff --git a/src/main/java/synapticloop/h2zero/base/manager/ConnectionManager.java b/src/main/java/synapticloop/h2zero/base/manager/mysql/ConnectionManager.java similarity index 99% rename from src/main/java/synapticloop/h2zero/base/manager/ConnectionManager.java rename to src/main/java/synapticloop/h2zero/base/manager/mysql/ConnectionManager.java index 10ba466a..a87d065e 100755 --- a/src/main/java/synapticloop/h2zero/base/manager/ConnectionManager.java +++ b/src/main/java/synapticloop/h2zero/base/manager/mysql/ConnectionManager.java @@ -1,4 +1,4 @@ -package synapticloop.h2zero.base.manager; +package synapticloop.h2zero.base.manager.mysql; /* * Copyright (c) 2012-2015 synapticloop. diff --git a/src/main/java/synapticloop/h2zero/base/manager/sqlite/ConnectionManager.java b/src/main/java/synapticloop/h2zero/base/manager/sqlite/ConnectionManager.java new file mode 100755 index 00000000..a82ce07c --- /dev/null +++ b/src/main/java/synapticloop/h2zero/base/manager/sqlite/ConnectionManager.java @@ -0,0 +1,557 @@ +package synapticloop.h2zero.base.manager.sqlite; + +/* + * Copyright (c) 2015 synapticloop. + * All rights reserved. + * + * This source code and any derived binaries are covered by the terms and + * conditions of the Licence agreement ("the Licence"). You may not use this + * source code or any derived binaries except in compliance with the Licence. + * A copy of the Licence is available in the file named LICENCE shipped with + * this source code or binaries. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * Licence for the specific language governing permissions and limitations + * under the Licence. + */ + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.sql.Types; + +import com.mchange.v2.c3p0.ComboPooledDataSource; + +public class ConnectionManager { + private static ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); + + private ConnectionManager() {} + + public static Connection getConnection() throws SQLException { + return(comboPooledDataSource.getConnection()); + } + + public static void closeAll(Connection connection) { + closeAll(null, null, connection); + } + + public static void closeAll(Statement statement, Connection connection) { + closeAll(null, statement, connection); + } + + public static void closeAll(ResultSet resultSet, Statement statement) { + closeAll(resultSet, statement, null); + } + + public static void closeAll(Statement statement) { + closeAll(null, statement, null); + } + + public static void closeAll(ResultSet resultSet, Statement statement, Connection connection) { + if(null != resultSet) { + try { + resultSet.close(); + } catch (SQLException jssqlex) { + // do nothing + } finally { + resultSet = null; + } + } + + if(null != statement) { + try { + statement.close(); + } catch (SQLException jssqlex) { + // do nothing + } finally { + statement = null; + } + } + + if(null != connection) { + try { + connection.close(); + } catch (SQLException jssqlex) { + // do nothing + } finally { + connection = null; + } + } + } + + /** + * Set a BIGINT datatype to a prepared statement with the value of the passed + * in Long, or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setBigint(PreparedStatement preparedStatement, int parameterIndex, Long value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.BIGINT); + } else { + preparedStatement.setLong(parameterIndex, value); + } + } + + /** + * Set a VARCHAR datatype to a prepared statement with the value of the passed + * in String, or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setVarchar(PreparedStatement preparedStatement, int parameterIndex, String value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.VARCHAR); + } else { + preparedStatement.setString(parameterIndex, value); + } + } + + /** + * Set a VARCHAR datatype to a prepared statement with the value of the passed + * in String, or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setClob(PreparedStatement preparedStatement, int parameterIndex, String value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.CLOB); + } else { + preparedStatement.setClob(parameterIndex, new StringReader(value)); + } + } + + /** + * Set a INT datatype to a prepared statement with the value of the passed + * in Integer, or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setInt(PreparedStatement preparedStatement, int parameterIndex, Integer value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.INTEGER); + } else { + preparedStatement.setInt(parameterIndex, value); + } + } + + /** + * Set a DATETIME datatype to a prepared statement with the value of the passed + * in timestamp or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setDatetime(PreparedStatement preparedStatement, int parameterIndex, Timestamp value) throws SQLException { + setTimestamp(preparedStatement, parameterIndex, value); + } + + public static void setDate(PreparedStatement preparedStatement, int parameterIndex, Date value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.DATE); + } else { + preparedStatement.setDate(parameterIndex, value); + } + } + + /** + * Set a TIMESTAMP datatype to a prepared statement with the value of the passed + * in timestamp, or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setTimestamp(PreparedStatement preparedStatement, int parameterIndex, Timestamp value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.TIMESTAMP); + } else { + preparedStatement.setTimestamp(parameterIndex, value); + } + } + + /** + * Set a MEDIUMTEXT datatype to a prepared statement with the value of the + * passed in mediumtext, or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setMediumtext(PreparedStatement preparedStatement, int parameterIndex, String value) throws SQLException { + setVarchar(preparedStatement, parameterIndex, value); + } + + /** + * Set a LONGTEXT datatype to a prepared statement with the value of the + * passed in longtext, or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setLongtext(PreparedStatement preparedStatement, int parameterIndex, String value) throws SQLException { + setVarchar(preparedStatement, parameterIndex, value); + } + + /** + * Set a FLOAT datatype to a prepared statement with the value of the passed + * in float, or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setFloat(PreparedStatement preparedStatement, int parameterIndex, Float value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.FLOAT); + } else { + preparedStatement.setFloat(parameterIndex, value); + } + } + + /** + * Set a TINYINT datatype to a prepared statement with the value of the passed + * in boolean, or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setTinyint(PreparedStatement preparedStatement, int parameterIndex, Boolean value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.TINYINT); + } else { + preparedStatement.setBoolean(parameterIndex, value); + } + } + + /** + * Set a BOOLEAN (or in sthis case conversion to a TINYINT) datatype to a + * prepared statement with the value of the passed in boolean, or the correct + * SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setBoolean(PreparedStatement preparedStatement, int parameterIndex, Boolean value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.TINYINT); + } else { + preparedStatement.setBoolean(parameterIndex, value); + } + } + + /** + * Set a DOUBLE datatype to a prepared statement with the value of the passed + * in double, or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setDouble(PreparedStatement preparedStatement, int parameterIndex, Double value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.DOUBLE); + } else { + preparedStatement.setDouble(parameterIndex, value); + } + } + + /** + * Set a CLOB datatype to a prepared statement with the value of the passed + * in clob, or the correct SQL null type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the value to be set + * + * @throws SQLException if something went horribly wrong + */ + public static void setClob(PreparedStatement preparedStatement, int parameterIndex, Clob value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.CLOB); + } else { + preparedStatement.setClob(parameterIndex, value); + } + } + + /** + * Set a CLOB datatype to a prepared statement with the value of the passed in inputStream, or the correct SQL null + * type if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param inputStream the inputStream to read from + * + * @throws SQLException if something went horribly wrong + */ + public static void setClob(PreparedStatement preparedStatement, int parameterIndex, InputStream inputStream) throws SQLException { + if(null == inputStream) { + preparedStatement.setNull(parameterIndex, Types.CLOB); + } else { + preparedStatement.setClob(parameterIndex, new InputStreamReader(inputStream)); + } + } + + /** + * Set a CLOB datatype to a prepared statement with the value of the passed in reader, or the correct SQL null type + * if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param reader the reader to use to stream the data + * + * @throws SQLException if something went horribly wrong + */ + public static void setClob(PreparedStatement preparedStatement, int parameterIndex, Reader reader) throws SQLException { + if(null == reader) { + preparedStatement.setNull(parameterIndex, Types.CLOB); + } else { + preparedStatement.setClob(parameterIndex, reader); + } + } + + /** + * Set a BLOB datatype to a prepared statement with the value of the passed in Blob, or the correct SQL null type + * if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the Blob value to insert + * + * @throws SQLException if something went horribly wrong + */ + public static void setBlob(PreparedStatement preparedStatement, int parameterIndex, Blob value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.BLOB); + } else { + preparedStatement.setBlob(parameterIndex, value); + } + } + + /** + * Set a BLOB datatype to a prepared statement with the value of the passed in Blob, or the correct SQL null type + * if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param inputStream the input stream to read from + * + * @throws SQLException if something went horribly wrong + */ + public static void setBlob(PreparedStatement preparedStatement, int parameterIndex, InputStream inputStream) throws SQLException { + if(null == inputStream) { + preparedStatement.setNull(parameterIndex, Types.BLOB); + } else { + preparedStatement.setBlob(parameterIndex, inputStream); + } + } + + /** + * Set a BLOB datatype to a prepared statement with the value of the passed in Blob, or the correct SQL null type + * if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param value the Blob value to insert + * + * @throws SQLException if something went horribly wrong + */ + public static void setMediumblob(PreparedStatement preparedStatement, int parameterIndex, Blob value) throws SQLException { + if(null == value) { + preparedStatement.setNull(parameterIndex, Types.BLOB); + } else { + preparedStatement.setBlob(parameterIndex, value); + } + } + + /** + * Set a Mediumblob datatype to a prepared statement with the value of the passed in Blob, or the correct SQL null type + * if null + * + * @param preparedStatement The prepared statement + * @param parameterIndex the index of the parameter + * @param inputStream the input stream to read from + * + * @throws SQLException if something went horribly wrong + */ + public static void setMediumblob(PreparedStatement preparedStatement, int parameterIndex, InputStream inputStream) throws SQLException { + if(null == inputStream) { + preparedStatement.setNull(parameterIndex, Types.NULL); + } else { + preparedStatement.setBlob(parameterIndex, inputStream); + } + } + + public static void setLongtext(PreparedStatement preparedStatement, int parameterIndex, Reader reader) throws SQLException { + if(null == reader) { + preparedStatement.setNull(parameterIndex, Types.LONGVARCHAR); + } else { + preparedStatement.setClob(parameterIndex, reader); + } + } + + public static void setLongtext(PreparedStatement preparedStatement, int parameterIndex, InputStream inputStream) throws SQLException { + setLongtext(preparedStatement, parameterIndex, new InputStreamReader(inputStream)); + } + + + /** + * Get an Long result from the resultSet as a value or null. In the case where the resulting value is null, this will + * be set to 0 (zero) by the jdbc driver. Consequently the resultSet is checked to see whether it was null. If so, + * null is returned, else the actual value + * + * @param resultSet The resultSet to get the value from + * @param index The index of the result + * @return the value, or null + * + * @throws SQLException if something went wrong + */ + public static Long getNullableResultLong(ResultSet resultSet, int index) throws SQLException { + Long temp = resultSet.getLong(index); + if(resultSet.wasNull()) { + return(null); + } else { + return(temp); + } + } + + /** + * Get an Int result from the resultSet as a value or null. In the case where the resulting value is null, this will + * be set to 0 (zero) by the jdbc driver. Consequently the resultSet is checked to see whether it was null. If so, + * null is returned, else the actual value + * + * @param resultSet The resultSet to get the value from + * @param index The index of the result + * @return the value, or null + * + * @throws SQLException if something went wrong + */ + public static Integer getNullableResultInt(ResultSet resultSet, int index) throws SQLException { + Integer temp = resultSet.getInt(index); + if(resultSet.wasNull()) { + return(null); + } else { + return(temp); + } + } + + /** + * Get a Boolean result from the resultSet as a value or null. In the case where the resulting value is null, this will + * be set to 0 (zero) by the jdbc driver. Consequently the resultSet is checked to see whether it was null. If so, + * null is returned, else the actual value + * + * @param resultSet The resultSet to get the value from + * @param index The index of the result + * @return the value, or null + * + * @throws SQLException if something went wrong + */ + public static Boolean getNullableResultBoolean(ResultSet resultSet, int index) throws SQLException { + Boolean temp = resultSet.getBoolean(index); + if(resultSet.wasNull()) { + return(null); + } else { + return(temp); + } + } + + + public static String clobReader(String fileName, Writer writerArg) { + String clobData = null; + + BufferedReader br = null; + + try { + br = new BufferedReader(new FileReader(fileName)); + String nextLine = ""; + StringBuilder sb = new StringBuilder(); + while ((nextLine = br.readLine()) != null) { + writerArg.write(nextLine); + sb.append(nextLine); + } + + // Convert the content into to a string + clobData = sb.toString(); + } catch (FileNotFoundException fnfex) { + // do nothing - return null + } catch (IOException ioex) { + // do nothing return null + } finally { + if(br != null) { + try { + br.close(); + } catch (IOException ex) { + // TODO Auto-generated catch block + ex.printStackTrace(); + } finally { + br = null; + } + } + } + + // Return the data. + return clobData; + } + + public static Clob getNullableResultClob(ResultSet resultSet, int index) throws SQLException { return(resultSet.getClob(index)); } + public static String getNullableResultString(ResultSet resultSet, int index) throws SQLException { return(resultSet.getString(index)); } + public static Blob getNullableResultBlob(ResultSet resultSet, int index) throws SQLException { return(resultSet.getBlob(index)); } + public static Timestamp getNullableResultTimestamp(ResultSet resultSet, int index) throws SQLException { return(resultSet.getTimestamp(index)); } + public static Date getNullableResultDate(ResultSet resultSet, int index) throws SQLException { return(resultSet.getDate(index)); } + public static Float getNullableResultFloat(ResultSet resultSet, int index) throws SQLException { return(resultSet.getFloat(index)); } + public static ComboPooledDataSource getComboPooledDataSource() { return comboPooledDataSource; } + +} diff --git a/src/main/java/synapticloop/h2zero/base/model/ModelBase.java b/src/main/java/synapticloop/h2zero/base/model/ModelBase.java index 103ec1f4..fcbc27ae 100755 --- a/src/main/java/synapticloop/h2zero/base/model/ModelBase.java +++ b/src/main/java/synapticloop/h2zero/base/model/ModelBase.java @@ -21,7 +21,7 @@ import java.sql.SQLException; import synapticloop.h2zero.base.exception.H2ZeroPrimaryKeyException; -import synapticloop.h2zero.base.manager.ConnectionManager; +import synapticloop.h2zero.base.manager.mysql.ConnectionManager; /** * This is the base class for all h2zero generated models and defines the required functionality for a working model. diff --git a/src/main/java/synapticloop/h2zero/base/monitor/C3P0PoolStatistics.java b/src/main/java/synapticloop/h2zero/base/monitor/C3P0PoolStatistics.java index 7cc744b9..b9542c8a 100755 --- a/src/main/java/synapticloop/h2zero/base/monitor/C3P0PoolStatistics.java +++ b/src/main/java/synapticloop/h2zero/base/monitor/C3P0PoolStatistics.java @@ -2,7 +2,7 @@ import java.sql.SQLException; -import synapticloop.h2zero.base.manager.ConnectionManager; +import synapticloop.h2zero.base.manager.mysql.ConnectionManager; import com.mchange.v2.c3p0.ComboPooledDataSource; diff --git a/src/main/java/synapticloop/h2zero/model/BaseSchemaObject.java b/src/main/java/synapticloop/h2zero/model/BaseSchemaObject.java index a6d7ad1c..75191446 100644 --- a/src/main/java/synapticloop/h2zero/model/BaseSchemaObject.java +++ b/src/main/java/synapticloop/h2zero/model/BaseSchemaObject.java @@ -16,6 +16,7 @@ import synapticloop.h2zero.model.field.BaseField; import synapticloop.h2zero.model.util.JSONKeyConstants; import synapticloop.h2zero.util.NamingHelper; +import synapticloop.h2zero.util.SimpleLogger; public abstract class BaseSchemaObject { protected JSONObject jsonObject; @@ -140,6 +141,11 @@ private void generateAutomaticFinder(String uniqueFinder, boolean unique) throws } } + protected void logFatalFieldParse(Exception exception, String message, String firstUpper) throws H2ZeroParseException { + SimpleLogger.logFatal(SimpleLogger.LoggerType.PARSE, exception.getClass().getSimpleName() + ": on table or view '" + this.name + "', throwing upwards..., for field synapticloop.h2zero.model.field." + firstUpper + "Field"); + throw new H2ZeroParseException(message, exception); + } + /** * Go through all of the fields and populate the referenced field types */ diff --git a/src/main/java/synapticloop/h2zero/model/Options.java b/src/main/java/synapticloop/h2zero/model/Options.java index 830f5cf3..ec79fdfc 100644 --- a/src/main/java/synapticloop/h2zero/model/Options.java +++ b/src/main/java/synapticloop/h2zero/model/Options.java @@ -18,6 +18,7 @@ public class Options { private boolean metrics = false; private String logging = ""; + private String database = "mysql"; private Set generators = new HashSet(); @@ -45,6 +46,12 @@ public class Options { ALLOWABLE_LOGGERS.add("log4j"); } + private static Set ALLOWABLE_DATABASES = new HashSet(); + static { + ALLOWABLE_DATABASES.add("mysql"); + ALLOWABLE_DATABASES.add("sqlite"); + } + public Options(JSONObject jsonObject) throws H2ZeroParseException { JSONObject optionsJson = null; try { @@ -56,6 +63,8 @@ public Options(JSONObject jsonObject) throws H2ZeroParseException { this.metrics = optionsJson.optBoolean("metrics", false); this.logging = optionsJson.optString("logging", ""); + this.database = optionsJson.optString("database", "mysql"); + SimpleLogger.logInfo(LoggerType.OPTIONS, "Generating for database type '" + database + "'."); JSONArray generatorArray = optionsJson.optJSONArray("generators"); if(null == generatorArray) { @@ -139,4 +148,6 @@ private void enableGenerators(JSONArray generatorArray) throws H2ZeroParseExcept public String getLogging() { return logging; } public void setLogging(String logging) { this.logging = logging; } public boolean hasLogging() { return(!"".equals(this.logging)); } + public String getDatabase() { return database; } + public void setDatabase(String database) { this.database = database; } } diff --git a/src/main/java/synapticloop/h2zero/model/Table.java b/src/main/java/synapticloop/h2zero/model/Table.java index e345d38e..1a97eb2e 100755 --- a/src/main/java/synapticloop/h2zero/model/Table.java +++ b/src/main/java/synapticloop/h2zero/model/Table.java @@ -16,7 +16,6 @@ import synapticloop.h2zero.model.util.JSONKeyConstants; import synapticloop.h2zero.util.JsonHelper; import synapticloop.h2zero.util.NamingHelper; -import synapticloop.h2zero.util.SimpleLogger; /** * The table encapsulates everything that is required for a single database table @@ -235,11 +234,6 @@ private void populateFields(JSONObject jsonObject) throws H2ZeroParseException { } } - private void logFatalFieldParse(Exception exception, String message, String firstUpper) throws H2ZeroParseException { - SimpleLogger.logFatal(SimpleLogger.LoggerType.PARSE, exception.getClass().getSimpleName() + ": on table '" + this.name + "', throwing upwards..., for field synapticloop.h2zero.model.field." + firstUpper + "Field"); - throw new H2ZeroParseException(message, exception); - } - private void populateUpdaters(JSONObject jsonObject) throws H2ZeroParseException { JSONArray updaterJson = new JSONArray(); try { diff --git a/src/main/java/synapticloop/h2zero/model/View.java b/src/main/java/synapticloop/h2zero/model/View.java index ed81b28c..257bb3f2 100644 --- a/src/main/java/synapticloop/h2zero/model/View.java +++ b/src/main/java/synapticloop/h2zero/model/View.java @@ -50,8 +50,8 @@ private void populateFields(JSONObject jsonObject) throws H2ZeroParseException { JSONArray fieldJson = new JSONArray(); try { fieldJson = jsonObject.getJSONArray(JSONKeyConstants.FIELDS); - } catch (JSONException ojjsonex) { - throw new H2ZeroParseException("Cannot create a view without '" + JSONKeyConstants.FIELDS + "'."); + } catch (JSONException jsonex) { + throw new H2ZeroParseException("Cannot create a view without '" + JSONKeyConstants.FIELDS + "'.", jsonex); } for (int i = 0; i < fieldJson.length(); i++) { @@ -62,8 +62,8 @@ private void populateFields(JSONObject jsonObject) throws H2ZeroParseException { fieldObject = fieldJson.getJSONObject(i); type = fieldObject.getString(JSONKeyConstants.TYPE); name = fieldObject.getString(JSONKeyConstants.NAME); - } catch (JSONException ojjsonex) { - throw new H2ZeroParseException("Could not parse the '" + JSONKeyConstants.FIELDS + "' array."); + } catch (JSONException jsonex) { + throw new H2ZeroParseException("Could not parse the '" + JSONKeyConstants.FIELDS + "' array.", jsonex); } if(null != type) { @@ -76,34 +76,20 @@ private void populateFields(JSONObject jsonObject) throws H2ZeroParseException { fields.add(baseField); fieldLookup.put(name, baseField); - // BaseField setBaseField = (BaseField)constructor.newInstance(fieldObject); - // setBaseField.suffixJavaName("Set"); - // BaseField whereBaseField = (BaseField)constructor.newInstance(fieldObject); - // whereBaseField.suffixJavaName("Where"); - // - // setFieldLookup.put(name, setBaseField); - // whereFieldLookup.put(name, whereBaseField); - } catch (ClassNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + } catch (ClassNotFoundException cnfex) { + logFatalFieldParse(cnfex, cnfex.getCause().getMessage(), firstUpper); } catch (SecurityException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + logFatalFieldParse(e, e.getCause().getMessage(), firstUpper); } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + logFatalFieldParse(e, e.getCause().getMessage(), firstUpper); } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + logFatalFieldParse(e, e.getCause().getMessage(), firstUpper); } catch (InstantiationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + logFatalFieldParse(e, e.getCause().getMessage(), firstUpper); } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + logFatalFieldParse(e, e.getCause().getMessage(), firstUpper); } catch (InvocationTargetException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + logFatalFieldParse(e, e.getCause().getMessage(), firstUpper); } } } diff --git a/src/main/java/synapticloop/h2zero/model/field/validator/BaseValidator.java b/src/main/java/synapticloop/h2zero/model/field/validator/BaseValidator.java deleted file mode 100755 index 279ebf4a..00000000 --- a/src/main/java/synapticloop/h2zero/model/field/validator/BaseValidator.java +++ /dev/null @@ -1,5 +0,0 @@ -package synapticloop.h2zero.model.field.validator; - -public abstract class BaseValidator { - -} diff --git a/src/main/java/synapticloop/h2zero/model/field/validator/StringValidator.java b/src/main/java/synapticloop/h2zero/model/field/validator/StringValidator.java deleted file mode 100755 index 9652c34c..00000000 --- a/src/main/java/synapticloop/h2zero/model/field/validator/StringValidator.java +++ /dev/null @@ -1,5 +0,0 @@ -package synapticloop.h2zero.model.field.validator; - -public class StringValidator extends BaseValidator { - -} diff --git a/src/main/java/synapticloop/h2zero/model/field/validator/ValidatorBase.java b/src/main/java/synapticloop/h2zero/model/field/validator/ValidatorBase.java new file mode 100755 index 00000000..90436728 --- /dev/null +++ b/src/main/java/synapticloop/h2zero/model/field/validator/ValidatorBase.java @@ -0,0 +1,71 @@ +package synapticloop.h2zero.model.field.validator; + +public abstract class ValidatorBase { + protected boolean requiresConfirm = false; + protected boolean allowNull = false; + protected int maxLength = Integer.MAX_VALUE; + protected int minLength = 0; + + public ValidatorBase(int minLength, int maxLength, boolean allowNull, boolean requiresConfirm) { + this.minLength = minLength; + this.maxLength = maxLength; + this.allowNull = allowNull; + this.requiresConfirm = requiresConfirm; + } + + public abstract boolean validate(String field, String fieldConfirm); + + public boolean isValid(String field) { + if(null == field) { + return(allowNull); + } else { + int length = field.length(); + return(length >= minLength && length <= maxLength); + } + } + /** + * Determine whether the field is valid with a confirmation field presented as well + * + * @param field + * @param fieldConfirm + * @return + */ + public boolean isValidConfirm(String field, String fieldConfirm) { + String tempField = convertToNullIfEmpty(field); + String tempFieldConfirm = convertToNullIfEmpty(fieldConfirm); + + // check for null and null + if(null == tempField) { + if(allowNull) { + return(null == tempFieldConfirm); + } else { + // we don't care what the confirm is at this point + return(false); + } + } + + // at this point tempField is not null + if(requiresConfirm) { + if(field.equals(tempFieldConfirm)) { + return(isValid(tempField)); + } + } else { + return(isValid(tempField)); + } + return(false); + } + + protected String convertToNullIfEmpty(String field) { + if(null == field) { + return(null); + } + + if(field.trim().length() == 0) { + return(null); + } + + return(field); + } + public boolean getAllowNull() { return this.allowNull; } + public void setAllowNull(boolean allowNull) { this.allowNull = allowNull; } +} diff --git a/src/main/java/synapticloop/h2zero/model/field/validator/ValidatorEmail.java b/src/main/java/synapticloop/h2zero/model/field/validator/ValidatorEmail.java new file mode 100644 index 00000000..402ddef1 --- /dev/null +++ b/src/main/java/synapticloop/h2zero/model/field/validator/ValidatorEmail.java @@ -0,0 +1,27 @@ +package synapticloop.h2zero.model.field.validator; + +import org.apache.commons.validator.routines.EmailValidator; + + +public class ValidatorEmail extends ValidatorBase { + + public ValidatorEmail(int minLength, int maxLength, boolean allowNull, boolean requiresConfirm) { + super(minLength, maxLength, allowNull, requiresConfirm); + } + + @Override + public boolean validate(String field, String fieldConfirm) { + boolean isValid = isValidConfirm(field, fieldConfirm); + + if(allowNull && isValid && null == convertToNullIfEmpty(field)) { + return(true); + } + + if(isValid) { + // do our validation + EmailValidator emailValidator = EmailValidator.getInstance(false); + return(emailValidator.isValid(field)); + } + return(false); + } +} diff --git a/src/main/java/synapticloop/h2zero/model/field/validator/ValidatorString.java b/src/main/java/synapticloop/h2zero/model/field/validator/ValidatorString.java new file mode 100755 index 00000000..b383b5de --- /dev/null +++ b/src/main/java/synapticloop/h2zero/model/field/validator/ValidatorString.java @@ -0,0 +1,15 @@ +package synapticloop.h2zero.model.field.validator; + +public class ValidatorString extends ValidatorBase { + + public ValidatorString(int minLength, int maxLength, boolean allowNull, boolean requiresConfirm) { + super(minLength, maxLength, allowNull, requiresConfirm); + } + + @Override + public boolean validate(String field, String confirmField) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/synapticloop/h2zero/validator/question/QuestionNameValidator.java b/src/main/java/synapticloop/h2zero/validator/question/QuestionNameValidator.java index 3d94c5f1..20b622b7 100644 --- a/src/main/java/synapticloop/h2zero/validator/question/QuestionNameValidator.java +++ b/src/main/java/synapticloop/h2zero/validator/question/QuestionNameValidator.java @@ -18,12 +18,13 @@ public QuestionNameValidator() { allowablePrefixNames.add("is"); allowablePrefixNames.add("has"); allowablePrefixNames.add("does"); - allowablePrefixList = "is, has, does"; + allowablePrefixNames.add("do"); + allowablePrefixList = "is, has, does, do"; } @Override public void parseAndValidateOptions(JSONObject optionsObject) { - parseAndValidateAllowablePrefixes(optionsObject, "is, has, does"); + parseAndValidateAllowablePrefixes(optionsObject, allowablePrefixList); } @Override diff --git a/src/main/template/ant-create-build.templar b/src/main/template/ant-create-build.templar index 8ae7543f..9ffee142 100755 --- a/src/main/template/ant-create-build.templar +++ b/src/main/template/ant-create-build.templar @@ -1,6 +1,6 @@ diff --git a/src/main/template/java-create-counter.templar b/src/main/template/java-create-counter.templar index c8931685..ba50e64f 100755 --- a/src/main/template/java-create-counter.templar +++ b/src/main/template/java-create-counter.templar @@ -15,7 +15,7 @@ import java.util.HashMap;{\n} import java.util.Map;{\n} import java.util.List;{\n} {\n} -import synapticloop.h2zero.base.manager.ConnectionManager;{\n} +import synapticloop.h2zero.base.manager.{options.database}.ConnectionManager;{\n} {\n} import org.apache.log4j.Level;{\n} import org.apache.log4j.Logger;{\n} diff --git a/src/main/template/java-create-database-checker-helper.templar b/src/main/template/java-create-database-checker-helper.templar index f5ae6c4b..22bd91fe 100644 --- a/src/main/template/java-create-database-checker-helper.templar +++ b/src/main/template/java-create-database-checker-helper.templar @@ -16,7 +16,7 @@ import java.util.Iterator;{\n} {\n} import org.apache.log4j.Logger;{\n} {\n} -import synapticloop.h2zero.base.manager.ConnectionManager;{\n} +import synapticloop.h2zero.base.manager.{options.database}.ConnectionManager;{\n} public class DatabaseCheckerHelper {{{\n} {\t}private static final Logger LOGGER = Logger.getLogger(DatabaseCheckerHelper.class);{\n}{\n} diff --git a/src/main/template/java-create-deleter.templar b/src/main/template/java-create-deleter.templar index f617da1b..d9a4dbfe 100755 --- a/src/main/template/java-create-deleter.templar +++ b/src/main/template/java-create-deleter.templar @@ -13,7 +13,7 @@ import java.sql.Connection;{\n} import java.sql.PreparedStatement;{\n} import java.sql.SQLException;{\n} {\n} -import synapticloop.h2zero.base.manager.ConnectionManager;{\n} +import synapticloop.h2zero.base.manager.{options.database}.ConnectionManager;{\n} import {database.package}.model.util.Constants;{\n} {\n} diff --git a/src/main/template/java-create-hit-count-servlet.templar b/src/main/template/java-create-hit-count-servlet.templar index 5b92c007..e3ba5af1 100644 --- a/src/main/template/java-create-hit-count-servlet.templar +++ b/src/main/template/java-create-hit-count-servlet.templar @@ -16,7 +16,7 @@ import java.util.Iterator;{\n} {\n} import org.apache.log4j.Logger;{\n} {\n} -import synapticloop.h2zero.base.manager.ConnectionManager;{\n} +import synapticloop.h2zero.base.manager.{options.database}.ConnectionManager;{\n} public class HitCountServlet {{{\n} {\t}private static final Logger LOGGER = Logger.getLogger(HitCountServlet.class);{\n}{\n} diff --git a/src/main/template/java-create-inserter.templar b/src/main/template/java-create-inserter.templar index 4e93dd0e..92cb42d0 100755 --- a/src/main/template/java-create-inserter.templar +++ b/src/main/template/java-create-inserter.templar @@ -21,7 +21,7 @@ import java.sql.SQLException;{\n} {if fn:requiresImport[table, "Blob"]}import java.sql.Blob;{\n}{endif} {if fn:requiresImport[table, "Timestamp"]}import java.sql.Timestamp;{\n}{endif} {\n} -import synapticloop.h2zero.base.manager.ConnectionManager;{\n} +import synapticloop.h2zero.base.manager.{options.database}.ConnectionManager;{\n} import {database.package}.model.util.Constants;{\n} {\n} @@ -39,7 +39,7 @@ public class {table.javaClassName}Inserter {{{\n} {set table as baseSchemaObject} {import classpath:/snippet/global/java-binder-declaration.templar} -{\t}// static fields generated by synapticloop h20{\n} +{\t}// static fields generated by synapticloop h2zero{\n} {\t}private static final String SQL_BUILTIN_INSERT_ALL = "insert into {table.name}( {loop table.fields as field} diff --git a/src/main/template/java-create-model.templar b/src/main/template/java-create-model.templar index f105c05a..3c6455c2 100755 --- a/src/main/template/java-create-model.templar +++ b/src/main/template/java-create-model.templar @@ -4,7 +4,7 @@ package {database.package}.model;{\n}{\n} // with the use of synapticloop templar templating language{\n} // (java-create-model.templar){\n}{\n} {if !table.isConstant} - import synapticloop.h2zero.base.manager.ConnectionManager;{\n} + import synapticloop.h2zero.base.manager.{options.database}.ConnectionManager;{\n} import synapticloop.h2zero.base.model.ModelBase;{\n} import synapticloop.h2zero.base.exception.H2ZeroPrimaryKeyException;{\n} import java.lang.StringBuilder;{\n} diff --git a/src/main/template/java-create-updater.templar b/src/main/template/java-create-updater.templar index 30d151ad..a98ae512 100755 --- a/src/main/template/java-create-updater.templar +++ b/src/main/template/java-create-updater.templar @@ -16,7 +16,7 @@ import java.sql.SQLException;{\n} import java.sql.Types;{\n} import java.sql.Timestamp;{\n} {\n} -import synapticloop.h2zero.base.manager.ConnectionManager;{\n}{\n} +import synapticloop.h2zero.base.manager.{options.database}.ConnectionManager;{\n} import {database.package}.finder.{table.javaClassName}Finder;{\n} import {database.package}.model.util.Constants;{\n} {\n} @@ -34,7 +34,7 @@ public class {table.javaClassName}Updater {{{\n} {set table as baseSchemaObject} {import classpath:/snippet/global/java-binder-declaration.templar} -{\t}// static fields generated by synapticloop h20{\n} +{\t}// static fields generated by synapticloop h2zero{\n} {\t}private static final String SQL_UPDATE_START = "update {table.name} ";{\n}{\n} {\t}// static fields generated from the user input{\n} diff --git a/src/main/template/snippet/global/finder-imports.templar b/src/main/template/snippet/global/finder-imports.templar index 09831f8d..ba809b6c 100644 --- a/src/main/template/snippet/global/finder-imports.templar +++ b/src/main/template/snippet/global/finder-imports.templar @@ -14,7 +14,7 @@ import java.util.Map;{\n} import java.util.ArrayList;{\n} {\n} import synapticloop.h2zero.base.exception.H2ZeroFinderException;{\n} -import synapticloop.h2zero.base.manager.ConnectionManager;{\n} +import synapticloop.h2zero.base.manager.{options.database}.ConnectionManager;{\n} {\n} import org.apache.log4j.Level;{\n} import org.apache.log4j.Logger;{\n} diff --git a/src/main/template/snippet/global/java-model-imports.templar b/src/main/template/snippet/global/java-model-imports.templar index 3deb636b..551a9275 100644 --- a/src/main/template/snippet/global/java-model-imports.templar +++ b/src/main/template/snippet/global/java-model-imports.templar @@ -8,7 +8,7 @@ import java.sql.SQLException;{\n} {if fn:requiresImport[table, "Timestamp"]}import java.sql.Timestamp;{\n}{endif} {\n} import synapticloop.h2zero.base.exception.H2ZeroFinderException;{\n} -import synapticloop.h2zero.base.manager.ConnectionManager;{\n} +import synapticloop.h2zero.base.manager.{options.database}.ConnectionManager;{\n} {\n} import org.apache.log4j.Level;{\n} import org.apache.log4j.Logger;{\n} diff --git a/src/test/java/sample.h2zero b/src/test/java/sample.h2zero index d2f75217..79a19e2f 100644 --- a/src/test/java/sample.h2zero +++ b/src/test/java/sample.h2zero @@ -36,6 +36,36 @@ ] }, + { + "name": "user_title", + "comments": [ + "This is the user title table, which is a constant-generated table for some ", + "of the user titles. This enables quick and easy lookups from within the code", + "for values that do not change." + ], + "fields": [ + { "name": "id_user_title", "type": "bigint", "nullable": false, "primary": true }, + { "name": "nm_user_title", "type": "varchar", "length": "32", "nullable": false }, + { "name": "num_order_by", "type": "int", "nullable": false }, + ], + "constants": [ + { "name": "MR", "values": [ 1, "Mr.", 1 ] }, + { "name": "MRS", "values": [ 2, "Mrs.", 2 ] }, + { "name": "MISS", "values": [ 3, "Miss", 3 ] }, + { "name": "DR", "values": [ 4, "Dr.", 4 ] } + ], + "finders": [ + { + "name": "findAllOrdered", + "selectClause": "select id_user_title, nm_user_title from user_title order by num_order_by", + "selectFields": [ + { "name": "id_user_title", "type": "bigint" }, + { "name": "nm_user_title", "type": "varchar" } + ], + "unique": false + } + ] + }, { "name": "user", @@ -188,6 +218,10 @@ { "name": "id_pet", "type": "bigint", "nullable": false, "index": true, "foreignKey": "pet.id_pet" } ] } + ], + + "forms": [ ] + } } \ No newline at end of file diff --git a/src/test/java/synapticloop/h2zero/MainTest.java b/src/test/java/synapticloop/h2zero/MainTest.java index 9f0369e2..fea4d7a9 100644 --- a/src/test/java/synapticloop/h2zero/MainTest.java +++ b/src/test/java/synapticloop/h2zero/MainTest.java @@ -5,12 +5,12 @@ public class MainTest { private Main main; - @Test(expected=IllegalArgumentException.class) + @Test public void testNullArguments() { Main.main(null); } - @Test(expected=IllegalArgumentException.class) + @Test public void testTwoArguments() { Main.main(new String[]{"one", "two"}); } diff --git a/src/test/java/synapticloop/h2zero/model/field/validator/ValidatorEmailTest.java b/src/test/java/synapticloop/h2zero/model/field/validator/ValidatorEmailTest.java new file mode 100644 index 00000000..4429ea48 --- /dev/null +++ b/src/test/java/synapticloop/h2zero/model/field/validator/ValidatorEmailTest.java @@ -0,0 +1,55 @@ +package synapticloop.h2zero.model.field.validator; + +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; + +public class ValidatorEmailTest { + private ValidatorEmail validatorEmailNoNullNoConfirm; + private ValidatorEmail validatorEmailAllowNullNoConfirm; + private ValidatorEmail validatorEmailAllowNullAllowConfirm; + private ValidatorEmail validatorEmailNoNullAllowConfirm; + + @Before + public void setup() { + validatorEmailNoNullNoConfirm = new ValidatorEmail(10, 30, false, false); + validatorEmailAllowNullNoConfirm = new ValidatorEmail(10, 30, true, false); + validatorEmailAllowNullAllowConfirm = new ValidatorEmail(10, 30, true, true); + validatorEmailNoNullAllowConfirm = new ValidatorEmail(10, 30, true, true); + } + + @Test + public void testValidEmails() { + assertTrue(validatorEmailNoNullNoConfirm.validate("a@example.com", null)); + assertTrue(validatorEmailNoNullNoConfirm.validate("a@example.com", "")); + + // these should all convert to null + assertTrue(validatorEmailAllowNullNoConfirm.validate(null, null)); + assertTrue(validatorEmailAllowNullNoConfirm.validate(" ", null)); + assertTrue(validatorEmailAllowNullNoConfirm.validate("", " ")); + assertTrue(validatorEmailAllowNullNoConfirm.validate("", "")); + assertTrue(validatorEmailAllowNullNoConfirm.validate("\t\n\t\r", "")); + + assertTrue(validatorEmailAllowNullAllowConfirm.validate(null, null)); + assertTrue(validatorEmailAllowNullAllowConfirm.validate("a@example.com", "a@example.com")); + + assertTrue(validatorEmailNoNullAllowConfirm.validate("a@example.com", "a@example.com")); + } + + public void testInvalidEmails() { + // too long + assertFalse(validatorEmailNoNullNoConfirm.validate("whilst-this-is-a-valid-email-this-is-too-long-for-our-field@example.com", null)); + + // is null and shouldn't be + assertFalse(validatorEmailNoNullNoConfirm.validate(null, null)); + + // too short + assertFalse(validatorEmailNoNullNoConfirm.validate("a@b.co", null)); + assertFalse(validatorEmailNoNullNoConfirm.validate("a@example.com", null)); + + // mis-match + assertFalse(validatorEmailNoNullAllowConfirm.validate("a@example.com", "something-else@something.com")); + } + +}