Commit 648100e2 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

Merge branch 'devel_12.0.x'

parents 53750fc7 f858e008
Pipeline #6003 passed with stage
in 1 minute and 52 seconds
......@@ -11,6 +11,18 @@ minerva (12.1.0~alpha.0) experimental; urgency=medium
-- Piotr Gawron <piotr.gawron@uni.lu> Wed, 25 Jul 2018 10:00:00 +0200
minerva (12.0.2) stable; urgency=medium
* Bug fix: data overlay by annotation type fixed
* Bug fix: [plugin] getting reactions with set of id larger than 100 elements
works properly
* Bug fix: name of commented protein was remembered for the new comment
* Bug fix: setting zoom level wasn't ranged and validated
* Bug fix: when uploading data overlay there is a warning regarding mixed new
line characters when necessary
* Performance: tomcat inside docker image by default can use 3G of memory
-- Piotr Gawron <piotr.gawron@uni.lu> Tue, 31 Jul 2018 13:00:00 +0200
minerva (12.0.1) stable; urgency=medium
* Bug fix: setting default zoom level on submap
* Bug fix: opened submap had different background then current selection
......
......@@ -45,6 +45,7 @@ ADD policy-rc.d /tmp/policy-rc.d
RUN apt-get update
RUN sed -i "/JAVA_OPTS=\"-Djava.awt.headless=true -Xmx128m -XX:+UseConcMarkSweepGC\"/c\JAVA_OPTS=\"-Djava.awt.headless=true -Xmx3096m -XX:+UseConcMarkSweepGC\"" /etc/default/tomcat7
#when we install minerva allow to start services, so everything can be installed properly
#but we need to disable tomcat7 because tomcat7 requires SYS_PTRACE and we cannot start it:
......
......@@ -27,7 +27,6 @@ import lcsb.mapviewer.model.map.MiriamType;
import lcsb.mapviewer.model.map.species.Chemical;
import lcsb.mapviewer.modelutils.map.ElementUtils;
import uk.ac.ebi.chebi.webapps.chebiWS.client.ChebiWebServiceClient;
import uk.ac.ebi.chebi.webapps.chebiWS.model.ChebiWebServiceFault_Exception;
import uk.ac.ebi.chebi.webapps.chebiWS.model.DataItem;
import uk.ac.ebi.chebi.webapps.chebiWS.model.Entity;
import uk.ac.ebi.chebi.webapps.chebiWS.model.LiteEntity;
......@@ -205,7 +204,7 @@ public class ChebiAnnotator extends ElementAnnotator implements IExternalService
}
}
}
} catch (ChebiWebServiceFault_Exception e) {
} catch (Exception e) {
throw new ChebiSearchException("Problem with chebi connection", e);
}
return null;
......@@ -290,7 +289,7 @@ public class ChebiAnnotator extends ElementAnnotator implements IExternalService
String value = miriamListToStringList(result);
setCacheValue(query, value);
} catch (ChebiWebServiceFault_Exception e) {
} catch (Exception e) {
throw new ChebiSearchException("Problem with chebi", e);
}
return result;
......@@ -300,7 +299,7 @@ public class ChebiAnnotator extends ElementAnnotator implements IExternalService
* Serialize list of chebi identifiers.
*
* @param list
* list of chebi identfiers
* list of chebi identifiers
* @return string with identifiers
*/
private String miriamListToStringList(List<MiriamData> list) {
......@@ -357,7 +356,6 @@ public class ChebiAnnotator extends ElementAnnotator implements IExternalService
}
try {
ChebiWebServiceClient client = getClient();
LiteEntityList entities = client.getLiteEntity(id, SearchCategory.CHEBI_ID, MAX_SEARCH_RESULTS_FROM_CHEBI_API,
StarsCategory.ALL);
List<LiteEntity> resultList = entities.getListElement();
......@@ -374,7 +372,7 @@ public class ChebiAnnotator extends ElementAnnotator implements IExternalService
setCacheValue("id: " + id, chebiSerializer.objectToString(result));
}
return result;
} catch (ChebiWebServiceFault_Exception e) {
} catch (Exception e) {
throw new ChebiSearchException("Problem with chebi", e);
}
}
......@@ -387,7 +385,7 @@ public class ChebiAnnotator extends ElementAnnotator implements IExternalService
* identifier): "CHEBI:XXXXX" or "XXXXX"
* @return common name of chemical
* @throws ChebiSearchException
* thrown when there is a problemw ith accessing information from
* thrown when there is a problem with accessing information from
* external chebi database
*/
protected String getChebiNameForChebiId(MiriamData id) throws ChebiSearchException {
......
......@@ -74,6 +74,7 @@ ServerConnector.init = function () {
self.removeListener("onDataLoadStop", listeners[i]);
}
self.MAX_NUMBER_OF_IDS_IN_GET_QUERY = 100;
};
ServerConnector.registerListenerType("onDataLoadStart");
ServerConnector.registerListenerType("onDataLoadStop");
......@@ -238,10 +239,19 @@ ServerConnector._sendRequest = function (params) {
* @returns {Promise}
*/
ServerConnector.sendPostRequest = function (url, params) {
var formParams = {};
for (var key in params) {
if (params.hasOwnProperty(key)) {
formParams[key] = this.objectToRequestString(params[key]);
if (formParams[key] === undefined) {
formParams[key] = params[key];
}
}
}
return this.sendRequest({
method: "POST",
url: url,
form: params
form: formParams
});
};
......@@ -289,6 +299,25 @@ ServerConnector.getServerBaseUrl = function () {
return this._serverBaseUrl;
};
/**
*
* @param {Object} object
* @returns {string}
*/
ServerConnector.objectToRequestString = function (object) {
var value;
if (object instanceof Point) {
value = this.pointToString(object);
} else if (Object.prototype.toString.call(object) === '[object Array]') {
value = this.idsToString(object);
} else if (typeof object === 'string' || object instanceof String || !isNaN(object)) {
value = object.toString();
} else {
value = undefined;
}
return value;
};
ServerConnector.createGetParams = function (params, prefix) {
var sorted = [], key;
......@@ -306,18 +335,12 @@ ServerConnector.createGetParams = function (params, prefix) {
if (prefix !== undefined) {
key = prefix + "." + key;
}
if (value instanceof Point) {
value = this.pointToString(value);
} else if (Object.prototype.toString.call(value) === '[object Array]') {
value = this.idsToString(value);
} else if (typeof value === 'string' || value instanceof String || !isNaN(value)) {
} else {
result += this.createGetParams(value, key);
value = undefined;
}
if (value !== undefined && value !== "") {
result += key + "=" + value + "&";
var serializedValue = this.objectToRequestString(value);
if (serializedValue === undefined) {
result += this.createGetParams(value, key);
} else if (serializedValue !== undefined && serializedValue !== "") {
result += key + "=" + serializedValue + "&";
}
}
return result;
......@@ -1513,6 +1536,15 @@ ServerConnector.getOverlayById = function (overlayId, projectId) {
});
};
/**
*
* @param {number} [params.modelId]
* @param {number[]} [params.ids]
* @param {number[]} [params.participantId]
* @param {string[]} [params.columns]
* @param {string} [params.projectId]
* @returns {*}
*/
ServerConnector.getReactions = function (params) {
var self = this;
var queryParams = {
......@@ -1531,7 +1563,7 @@ ServerConnector.getReactions = function (params) {
};
return self.getProjectId(params.projectId).then(function (result) {
queryParams.projectId = result;
if (filterParams.id.length > 100 || filterParams.participantId.length > 100) {
if (filterParams.id.length > self.MAX_NUMBER_OF_IDS_IN_GET_QUERY || filterParams.participantId.length > self.MAX_NUMBER_OF_IDS_IN_GET_QUERY) {
return self.sendPostRequest(self.getReactionsUrl(queryParams), filterParams);
} else {
return self.sendGetRequest(self.getReactionsUrl(queryParams, filterParams));
......@@ -1571,7 +1603,7 @@ ServerConnector.getAliases = function (params) {
};
return self.getProjectId(params.projectId).then(function (result) {
queryParams.projectId = result;
if (filterParams.id.length > 100) {
if (filterParams.id.length > self.MAX_NUMBER_OF_IDS_IN_GET_QUERY) {
return self.sendPostRequest(self.getAliasesUrl(queryParams), filterParams);
} else {
return self.sendGetRequest(self.getAliasesUrl(queryParams, filterParams));
......
......@@ -154,6 +154,10 @@ AddOverlayDialog.prototype.processFile = function (file) {
if (overlay.getType() !== undefined) {
typeSelect.val(overlay.getType());
}
if (overlayParser.containsMixedNewLineCharacters(evt.target.result)) {
GuiConnector.warn("Selected file contains new line characters from different operating systems " +
"(MAC/Windows/Linux). This might cause confusion when reading the file in the editor later on.")
}
resolve(self.getFileContent());
} catch (error) {
reject(error);
......
......@@ -38,13 +38,13 @@ function createRow(elements) {
return row;
}
CommentDialog.prototype.open = function (types) {
CommentDialog.prototype.open = function (identifiedElements) {
var self = this;
self.setTypes([CommentDialog.GENERAL]);
var promises = [CommentDialog.GENERAL];
for (var i = 0; i < types.length; i++) {
var ie = types[i];
for (var i = 0; i < identifiedElements.length; i++) {
var ie = identifiedElements[i];
if (ie.getType() === "ALIAS") {
promises.push(self.getMap().getSubmapById(ie.getModelId()).getModel().getAliasById(ie.getId(), true));
} else if (ie.getType() === "REACTION") {
......@@ -182,6 +182,7 @@ CommentDialog.prototype.setTypes = function (types) {
typeOptions.value = 0;
this._types = types;
typeOptions.onchange();
};
CommentDialog.prototype.getTypes = function () {
......
......@@ -114,7 +114,7 @@ EditProjectDialog.prototype.addTab = function (params) {
navigationObject: navLi,
navigationBar: params.tabMenuDiv
});
$(contentDiv).css("overflow","auto");
$(contentDiv).css("overflow", "auto");
if (params.content !== undefined) {
contentDiv.appendChild(params.content);
......@@ -821,7 +821,12 @@ function getValueOrEmpty(value) {
}
}
EditProjectDialog.prototype.mapToTableRow = function (map, users) {
/**
*
* @param {MapModel} map
* @returns {Array}
*/
EditProjectDialog.prototype.mapToTableRow = function (map) {
var row = [];
var id = map.getId();
var centerX = getValueOrEmpty(map.getDefaultCenterX());
......@@ -831,7 +836,17 @@ EditProjectDialog.prototype.mapToTableRow = function (map, users) {
row[1] = map.getName();
row[2] = "<input name='defaultCenterX-" + id + "' value='" + centerX + "'/>";
row[3] = "<input name='defaultCenterY-" + id + "' value='" + centerY + "'/>";
row[4] = "<input name='defaultZoomLevel-" + id + "' value='" + zoomLevel + "'/>";
var zoomLevelSelect = "<select name='defaultZoomLevel-" + id + "' value='" + zoomLevel + "'>" +
"<option value=''>---</option>";
for (var i = map.getMinZoom(); i <= map.getMaxZoom(); i++) {
var selected = "";
if (i === zoomLevel) {
selected = " selected ";
}
zoomLevelSelect += "<option value='" + i + "' " + selected + ">" + (i - map.getMinZoom()) + "</option>";
}
zoomLevelSelect += "</select>";
row[4] = zoomLevelSelect;
row[5] = "<button name='saveMap' data='" + id + "'><i class=\"fa fa-save\" style=\"font-size:17px\"></i></button>";
return row;
......@@ -921,8 +936,10 @@ EditProjectDialog.prototype.saveOverlay = function (overlayId) {
EditProjectDialog.prototype.saveMap = function (mapId) {
var self = this;
var map = self._mapsById[mapId];
map.setDefaultCenterX($("[name='defaultCenterX-" + mapId + "']", self.getElement())[0].value);
map.setDefaultCenterY($("[name='defaultCenterY-" + mapId + "']", self.getElement())[0].value);
var centerX = parseInt($("[name='defaultCenterX-" + mapId + "']", self.getElement())[0].value);
var centerY = parseInt($("[name='defaultCenterY-" + mapId + "']", self.getElement())[0].value);
map.setDefaultCenterX(centerX);
map.setDefaultCenterY(centerY);
map.setDefaultZoomLevel($("[name='defaultZoomLevel-" + mapId + "']", self.getElement())[0].value);
return ServerConnector.updateModel({projectId: self.getProject().getProjectId(), model: map});
......
......@@ -9,13 +9,24 @@ function OverlayParser() {
/**
*
* @param {string| Uint8Array|ArrayBuffer} content
* @returns {DataOverlay}
* @param {string| Uint8Array | ArrayBuffer} content
* @returns {string}
* @private
*/
OverlayParser.prototype.parse = function (content) {
OverlayParser.prototype._extractContent = function (content) {
if (content instanceof Uint8Array || content instanceof ArrayBuffer) {
content = new TextDecoder("UTF8").decode(content);
}
return content;
};
/**
*
* @param {string| Uint8Array|ArrayBuffer} content
* @returns {DataOverlay}
*/
OverlayParser.prototype.parse = function (content) {
content = this._extractContent(content);
var data = {content: content};
var lines = content.split("\n");
for (var i = 0; i < lines.length; i++) {
......@@ -42,5 +53,36 @@ OverlayParser.prototype.parse = function (content) {
return new DataOverlay(data);
};
/**
*
* @param {string| Uint8Array | ArrayBuffer} content
* @returns {boolean}
*/
OverlayParser.prototype.containsMixedNewLineCharacters = function (content) {
content = this._extractContent(content);
var newLineRegEx = /[\r\n]+/g;
var match = newLineRegEx.exec(content);
var newLineFormats = {};
var counter = 0;
while (match !== null && match !== undefined) {
var foundMultiplication = false;
var key = '';
//this is just a heuristic - let's assume there are at most 10 empty lines in a file
for (var i = 0; i < 10; i++) {
key += match[0];
if (newLineFormats[key]) {
foundMultiplication = true;
} else {
newLineFormats[key] = true;
}
}
if (!foundMultiplication) {
counter++;
}
match = newLineRegEx.exec(content);
}
return counter > 1;
};
module.exports = OverlayParser;
......@@ -455,8 +455,16 @@ MapModel.prototype.getDefaultZoomLevel = function () {
return this._defaultZoomLevel;
};
/**
*
* @param {number} defaultZoomLevel
*/
MapModel.prototype.setDefaultZoomLevel = function (defaultZoomLevel) {
this._defaultZoomLevel = defaultZoomLevel;
if (!isNaN(defaultZoomLevel)) {
this._defaultZoomLevel = defaultZoomLevel;
} else {
this._defaultZoomLevel = null;
}
};
/**
......@@ -467,8 +475,16 @@ MapModel.prototype.getDefaultCenterX = function () {
return this._defaultCenterX;
};
/**
*
* @param {number} defaultCenterX
*/
MapModel.prototype.setDefaultCenterX = function (defaultCenterX) {
this._defaultCenterX = defaultCenterX;
if (!isNaN(defaultCenterX)) {
this._defaultCenterX = defaultCenterX;
} else {
this._defaultCenterX = null;
}
};
/**
......@@ -480,7 +496,11 @@ MapModel.prototype.getDefaultCenterY = function () {
};
MapModel.prototype.setDefaultCenterY = function (defaultCenterY) {
this._defaultCenterY = defaultCenterY;
if (!isNaN(defaultCenterY)) {
this._defaultCenterY = defaultCenterY;
} else {
this._defaultCenterY = null;
}
};
MapModel.prototype.getSubmodelType = function () {
......
......@@ -13,6 +13,7 @@ var LayoutAlias = require('../../main/js/map/data/LayoutAlias');
var MapModel = require('../../main/js/map/data/MapModel');
var NetworkError = require('../../main/js/NetworkError');
var Project = require('../../main/js/map/data/Project');
var Point = require('../../main/js/map/canvas/Point');
var Reaction = require('../../main/js/map/data/Reaction');
var ServerConnector = require('../../main/js/ServerConnector');
var SecurityError = require('../../main/js/SecurityError');
......@@ -110,7 +111,7 @@ describe('ServerConnector', function () {
});
});
it('without ids', function () {
return ServerConnector.getReactions([]).then(function (result) {
return ServerConnector.getReactions({}).then(function (result) {
assert.equal(result.length, 28);
var reaction = result[0];
assert.ok(reaction instanceof Reaction);
......@@ -118,6 +119,32 @@ describe('ServerConnector', function () {
assert.equal(reaction.getModelId(), 15781);
});
});
it('with ids', function () {
return ServerConnector.getReactions({
modelId: 15781,
participantId: [329156, 329157, 329158, 329159, 329160, 329161, 329162, 329163, 329164, 329165, 329166, 329167, 329168, 329169, 329170, 329171, 329172, 329173, 329174, 329175, 329176, 329177, 329178, 329179, 329180, 329181, 329182, 329183, 329184, 329185]
}).then(function (result) {
assert.equal(result.length, 28);
var reaction = result[0];
assert.ok(reaction instanceof Reaction);
assert.equal(reaction.getId(), 153524);
assert.equal(reaction.getModelId(), 15781);
});
});
it('with ids when asking for too many elements for gett query', function () {
ServerConnector.MAX_NUMBER_OF_IDS_IN_GET_QUERY = 0;
return ServerConnector.getReactions({
modelId: 15781,
participantId: [329167]
}).then(function (result) {
assert.equal(result.length, 5);
var reaction = result[0];
assert.ok(reaction instanceof Reaction);
assert.equal(reaction.getId(), 153518);
assert.equal(reaction.getModelId(), 15781);
});
});
});
......@@ -420,4 +447,23 @@ describe('ServerConnector', function () {
});
describe('objectToRequestString', function () {
it('string', function () {
assert.equal("test_string", ServerConnector.objectToRequestString("test_string"));
});
it('number', function () {
assert.equal("12", ServerConnector.objectToRequestString(12));
});
it('point', function () {
assert.equal("0.00,10.00", ServerConnector.objectToRequestString(new Point(0, 10)));
});
it('array of ids', function () {
assert.equal("1,4", ServerConnector.objectToRequestString([1, 4]));
});
it('not sorted array of ids', function () {
assert.equal("1,4,5", ServerConnector.objectToRequestString([5, 1, 4]));
});
});
});
......@@ -42,5 +42,50 @@ describe('OverlayParser', function () {
});
});
});
describe('containsMixedNewLineCharacters', function () {
it('normal file', function () {
return ServerConnector.sendGetRequest("testFiles/overlay/good.txt").then(function (fileContent) {
var parser = new OverlayParser();
assert.notOk(parser.containsMixedNewLineCharacters(fileContent));
});
});
it('windows/linux mix', function () {
var content = "line1\n\rline2\n";
var parser = new OverlayParser();
assert.ok(parser.containsMixedNewLineCharacters(content));
});
it('windows empty lines', function () {
var content = "line1\n\rline2\n\r\n\rline4";
var parser = new OverlayParser();
assert.notOk(parser.containsMixedNewLineCharacters(content));
});
it('windows/mac mix', function () {
var content = "line1\n\rline2\r";
var parser = new OverlayParser();
assert.ok(parser.containsMixedNewLineCharacters(content));
});
it('mac empty lines', function () {
var content = "line1\rline2\r\rline4";
var parser = new OverlayParser();
assert.notOk(parser.containsMixedNewLineCharacters(content));
});
it('windows/mac mix 2', function () {
var content = "line1\n\r\rline2\n\rline3";
var parser = new OverlayParser();
assert.ok(parser.containsMixedNewLineCharacters(content));
});
it('linux empty lines', function () {
var content = "line1\nline2\n\nline4";
var parser = new OverlayParser();
assert.notOk(parser.containsMixedNewLineCharacters(content));
});
});
});
[{"centerPoint":{"x":142.99978766600722,"y":179.00261878591084},"hierarchyVisibilityLevel":null,"id":153518,"kineticLaw":null,"lines":[{"start":{"x":141.01308884552796,"y":203.5052375718217},"end":{"x":142.676524190943,"y":182.98953497836953},"type":"START"},{"start":{"x":143.32305114107143,"y":175.01570259345215},"end":{"x":144.98648648648648,"y":154.5},"type":"END"}],"modelId":15781,"modifiers":[],"notes":"","products":[{"aliasId":329167,"stoichiometry":null}],"reactants":[{"aliasId":329169,"stoichiometry":null}],"reactionId":"re8","references":[],"type":"State transition"},{"centerPoint":{"x":92.15973884958811,"y":136.13620918163832},"hierarchyVisibilityLevel":null,"id":153512,"kineticLaw":null,"lines":[{"start":{"x":75.73715058611361,"y":134.3476104598738},"end":{"x":88.18325303297577,"y":135.7031265679479},"type":"START"},{"start":{"x":96.13622466620043,"y":136.56929179532878},"end":{"x":108.58232711306259,"y":137.92480790340286},"type":"END"}],"modelId":15781,"modifiers":[],"notes":"","products":[{"aliasId":329167,"stoichiometry":null}],"reactants":[{"aliasId":329168,"stoichiometry":null}],"reactionId":"re4","references":[],"type":"State transition"},{"centerPoint":{"x":128.5,"y":47.0},"hierarchyVisibilityLevel":null,"id":153510,"kineticLaw":null,"lines":[{"start":{"x":92.0,"y":36.98039215686274},"end":{"x":124.64269540833152,"y":45.94113207287532},"type":"START"},{"start":{"x":132.35730459166848,"y":48.05886792712468},"end":{"x":165.0,"y":57.01960784313726},"type":"END"},{"start":{"x":144.03021943732247,"y":129.5},"end":{"x":131.99804583067973,"y":53.145215648491444},"type":"MIDDLE"}],"modelId":15781,"modifiers":[{"aliasId":329167,"stoichiometry":null}],"notes":"","products":[{"aliasId":329179,"stoichiometry":null}],"reactants":[{"aliasId":329173,"stoichiometry":null}],"reactionId":"re5","references":[],"type":"State transition"},{"centerPoint":{"x":172.17324895317853,"y":109.17253521126761},"hierarchyVisibilityLevel":null,"id":153504,"kineticLaw":null,"lines":[{"start":{"x":155.9662162162162,"y":129.5},"end":{"x":169.67962640764847,"y":112.300129590407},"type":"START"},{"start":{"x":174.6668714987086,"y":106.04494083212823},"end":{"x":188.38028169014086,"y":88.84507042253522},"type":"END"}],"modelId":15781,"modifiers":[],"notes":"","products":[{"aliasId":329179,"stoichiometry":null}],"reactants":[{"aliasId":329167,"stoichiometry":null}],"reactionId":"re3","references":[],"type":"State transition"},{"centerPoint":{"x":196.68292177751908,"y":142.47367216614504},"hierarchyVisibilityLevel":null,"id":153499,"kineticLaw":null,"lines":[{"start":{"x":180.0362865221489,"y":142.31809613572102},"end":{"x":192.68309645382215,"y":142.43629062106376},"type":"START"},{"start":{"x":200.68274710121602,"y":142.5110537112263},"end":{"x":213.32955703288923,"y":142.62924819656908},"type":"END"}],"modelId":15781,"modifiers":[],"notes":"","products":[{"aliasId":329172,"stoichiometry":null}],"reactants":[{"aliasId":329167,"stoichiometry":null}],"reactionId":"re10","references":[],"type":"State transition"}]
\ No newline at end of file
......@@ -221,7 +221,14 @@ public class ColorModelCommand extends ModelCommand {
}
}
for (MiriamData md : schema.getMiriamData()) {
if (!element.getMiriamData().contains(md)) {
boolean found = false;
for (MiriamData elementMiriamData : element.getMiriamData()) {
if (elementMiriamData.getDataType().equals(md.getDataType())
&& elementMiriamData.getResource().equals(md.getResource())) {
found = true;
}
}
if (!found) {
return false;
}
}
......
......@@ -360,6 +360,61 @@ public class ColorModelCommandTest extends CommandTestFunctions {
}
@Test
public void testSpeciesMatchWithMiriamData() throws Exception {
try {
GenericColorSchema colorSchema = new GenericColorSchema();
colorSchema.setName("s1");
colorSchema.addMiriamData(new MiriamData(MiriamType.HGNC_SYMBOL, "SNCA"));
GenericColorSchema colorSchema2 = new GenericColorSchema();
colorSchema2.setName("s1");
colorSchema2.addMiriamData(new MiriamData(MiriamType.HGNC_SYMBOL, "PARK7"));
GenericProtein species = new GenericProtein("id");
species.setName("s1");
species.addMiriamData(new MiriamData(MiriamType.HGNC_SYMBOL, "SNCA"));
List<ColorSchema> schemas = new ArrayList<>();
schemas.add(colorSchema);
ColorModelCommand factory = new ColorModelCommand(new ModelFullIndexed(null), schemas, colorExtractor);
assertTrue(factory.match(species, colorSchema));
assertFalse(factory.match(species, colorSchema2));
} catch (Exception e) {
e.printStackTrace();
throw e;
}