Commit 27f759d8 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

Merge branch '1316-dapi-over-ssl' into 'devel_15.0.x'

Resolve "when dapi is down minerva crash"

See merge request !1198
parents 8faf57c2 b0ae20b2
Pipeline #29078 passed with stage
in 16 minutes and 32 seconds
minerva (15.0.1) stable; urgency=medium
* Bug fix: when there was problem with DAPI minerva threw unexpected error
(#1316)
* Bug fix: DAPI connection must go over HTTPS (#1316)
-- Piotr Gawron <piotr.gawron@uni.lu> Fri, 26 Jun 2020 11:00:00 +0200
minerva (15.0.0) stable; urgency=medium
* Backward incompatible: old connection to CTD is removed and replaced with
new Data-API interface that closely check license compliance
......
......@@ -26,7 +26,7 @@ public class DapiConnectorImpl extends CachableInterface implements DapiConnecto
private static Logger logger = LogManager.getLogger();
private static final String DAPI_DOMAIN = "dapi.lcsb.uni.lu";
private static final String DAPI_BASE_URL = "http://" + DAPI_DOMAIN + "/api/";
private static final String DAPI_BASE_URL = "https://" + DAPI_DOMAIN + "/api/";
private ConfigurationDao configurationDao;
......@@ -220,6 +220,7 @@ public class DapiConnectorImpl extends CachableInterface implements DapiConnecto
List<String> result = new ArrayList<>();
String url = DAPI_BASE_URL + "database/";
String content = getWebPageDownloader().getFromNetwork(url);
logger.debug(content);
JsonArray array = new JsonParser()
.parse(content)
.getAsJsonObject()
......@@ -230,7 +231,7 @@ public class DapiConnectorImpl extends CachableInterface implements DapiConnecto
result.add(array.get(i).getAsJsonObject().get("name").getAsString());
}
return result;
} catch (IOException e) {
} catch (Exception e) {
throw new DapiConnectionException("Problem with accessing dapi", e);
}
}
......
......@@ -91,7 +91,6 @@ public class MiriamConnectorTest extends AnnotationTestFunctions {
}
if (!deprecated) {
assertTrue("Invalid MiriamType (" + mt + ") for MiriamType: " + mt, miriamConnector.isValidMiriamType(mt));
}
}
......
......@@ -26,7 +26,7 @@ public class CtdDapiConfiguration {
public DapiConnector dapiConnector() throws DapiConnectionException {
DapiConnectorImpl mock = (DapiConnectorImpl) Mockito.spy(dapiConnector);
Mockito.doReturn("http://dapi.lcsb.uni.lu/api/database/CTD/releases/2019.02/").when(mock)
Mockito.doReturn("https://dapi.lcsb.uni.lu/api/database/CTD/releases/2019.02/").when(mock)
.getLatestReleaseUrl("CTD");
Mockito.doReturn(true).when(mock).isValidConnection();
......
......@@ -26,7 +26,7 @@ public class DrugBankDapiConfiguration {
public DapiConnector dapiConnector() throws DapiConnectionException {
DapiConnectorImpl mock = (DapiConnectorImpl) Mockito.spy(dapiConnector);
Mockito.doReturn("http://dapi.lcsb.uni.lu/api/database/DrugBank/releases/5.1/").when(mock)
Mockito.doReturn("https://dapi.lcsb.uni.lu/api/database/DrugBank/releases/5.1/").when(mock)
.getLatestReleaseUrl("DrugBank");
Mockito.doReturn(true).when(mock).isValidConnection();
......
......@@ -266,17 +266,18 @@ final public class XmlParser {
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty(OutputKeys.METHOD, "xml");
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node element = list.item(i);
t.transform(new DOMSource(element), new StreamResult(sw));
if (includeHeadNode) {
t.transform(new DOMSource(node), new StreamResult(sw));
} else {
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node element = list.item(i);
t.transform(new DOMSource(element), new StreamResult(sw));
}
}
} catch (TransformerException te) {
logger.debug("nodeToString Transformer Exception");
}
if (includeHeadNode) {
return "<" + node.getNodeName() + ">" + sw.toString() + "</" + node.getNodeName() + ">";
}
return sw.toString();
}
......
......@@ -99,6 +99,14 @@ public class XmlParserTest extends CommonTestFunctions {
assertTrue(str.contains(xml));
}
@Test
public void testNodeToStringWithHeaderAndAttrib() throws Exception {
String xml = "<test_node x=\"y\">test_x</test_node>";
Node validDoc = XmlParser.getXmlDocumentFromString(xml).getDocumentElement();
String str = XmlParser.nodeToString(validDoc, true);
assertTrue(str.contains(xml));
}
@Test
public void testConcurrencyParse() throws Exception {
StringBuilder builder = new StringBuilder("<doc>");
......
......@@ -393,7 +393,7 @@ public class CellDesignerXmlParser extends Converter {
}
private void processModelRdfNode(Model model, Node node) throws InvalidXmlSchemaException {
XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser();
XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser(CommonXmlParser.RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER);
model.addMiriamData(xmlAnnotationParser.parseRdfNode(node));
model.addAuthors(xmlAnnotationParser.getAuthorsFromRdf(node));
model.setCreationDate(xmlAnnotationParser.getCreateDateFromRdf(node));
......@@ -728,7 +728,8 @@ public class CellDesignerXmlParser extends Converter {
result.append(layerParser.layerCollectionToXml(model.getLayers()));
result.append("</celldesigner:extension>\n");
XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser();
XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser(CommonXmlParser.RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER);
result.append(xmlAnnotationParser.dataSetToXmlString(model.getMiriamData(), model.getAuthors(),
model.getCreationDate(), model.getModificationDates(), model.getIdModel()));
result.append("</annotation>\n");
......
......@@ -4,6 +4,7 @@ import java.awt.Color;
import java.awt.geom.Dimension2D;
import java.awt.geom.Point2D;
import java.io.ByteArrayInputStream;
import java.util.*;
import javax.xml.parsers.*;
......@@ -15,6 +16,7 @@ import lcsb.mapviewer.common.XmlParser;
import lcsb.mapviewer.common.exception.InvalidStateException;
import lcsb.mapviewer.converter.model.celldesigner.structure.fields.SingleLine;
import lcsb.mapviewer.converter.model.celldesigner.structure.fields.View;
import lcsb.mapviewer.model.map.MiriamRelationType;
/**
* Class with parsers for common CellDesigner objects.
......@@ -47,6 +49,13 @@ public class CommonXmlParser {
}
public static Set<MiriamRelationType> RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER;
{
RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER = new HashSet<>();
RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER.addAll(Arrays.asList(MiriamRelationType.values()));
RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER.remove(MiriamRelationType.BQ_MODEL_IS_DERIVED_FROM);
}
private DocumentBuilderFactory dbFactory;
private DocumentBuilder dBuilder;
......
......@@ -496,16 +496,6 @@ public class LayerXmlParser {
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (node.getNodeName().equalsIgnoreCase("celldesigner:layerNotes")) {
String notes = XmlParser.getNodeValue(node).trim();
Color backgroundColor = extractBackgroundColor(notes);
if (backgroundColor != null) {
result.setBackgroundColor(backgroundColor);
notes = removeBackgroundColor(notes);
}
Color borderColor = extractBorderColor(notes);
if (borderColor != null) {
result.setBorderColor(borderColor);
notes = removeBorderColor(notes);
}
result.setNotes(notes);
} else if (node.getNodeName().equalsIgnoreCase("celldesigner:paint")) {
result.setColor(commonParser.getColor(node));
......@@ -545,25 +535,6 @@ public class LayerXmlParser {
return result.toString();
}
Color extractBackgroundColor(String notes) {
return extractColor(notes, "BackgroundColor");
}
Color extractBorderColor(String notes) {
return extractColor(notes, "BorderColor");
}
private Color extractColor(String notes, String string) {
String lines[] = notes.split("[\n\r]+");
for (String line : lines) {
if (line.startsWith(string + "=") || line.startsWith(string + ":")) {
String colorString = line.replace(string + "=", "").replace(string + ":", "");
return new ColorParser().parse(colorString);
}
}
return null;
}
/**
* Transforms LayerText object into CellDesigner xml node.
*
......
package lcsb.mapviewer.converter.model.celldesigner.annotation;
import lcsb.mapviewer.model.map.*;
import lcsb.mapviewer.model.map.layout.graphics.LayerText;
import lcsb.mapviewer.model.map.reaction.Reaction;
import lcsb.mapviewer.model.map.species.Element;
import lcsb.mapviewer.model.map.species.Species;
......@@ -134,6 +135,12 @@ public enum NoteField {
@ImportOnly
TRANSPARENCY_ZOOM_LEVEL_VISIBILITY("SemanticZoomLevelTransparency", Element.class, null),
@ImportOnly
BACKGROUND_COLOR("BackgroundColor", LayerText.class, null),
@ImportOnly
BORDER_COLOR("BorderColor", LayerText.class, null),
/**
* {@link Element#getSemanticZoomLevelVisibility()}.
*/
......
package lcsb.mapviewer.converter.model.celldesigner.annotation;
import java.awt.Color;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
......@@ -14,7 +15,11 @@ import org.w3c.dom.Node;
import lcsb.mapviewer.common.XmlParser;
import lcsb.mapviewer.common.comparator.*;
import lcsb.mapviewer.common.exception.*;
import lcsb.mapviewer.common.geometry.ColorParser;
import lcsb.mapviewer.converter.annotation.XmlAnnotationParser;
import lcsb.mapviewer.converter.model.celldesigner.CommonXmlParser;
import lcsb.mapviewer.model.LogMarker;
import lcsb.mapviewer.model.ProjectLogEntryType;
import lcsb.mapviewer.model.map.*;
import lcsb.mapviewer.model.map.layout.graphics.LayerText;
import lcsb.mapviewer.model.map.reaction.Reaction;
......@@ -44,13 +49,17 @@ public class RestAnnotationParser {
/**
* Parser used for parsing annotations in rdf format.
*/
private XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser();
private XmlAnnotationParser xmlAnnotationParser;
/**
* Class used for some simple operations on {@link BioEntity} elements.
*/
private ElementUtils elementUtils = new ElementUtils();
public RestAnnotationParser() {
xmlAnnotationParser = new XmlAnnotationParser(CommonXmlParser.RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER);
}
/**
* This method parse the string with annotations provided by the lcsb team.
* Parsing has been prepared based on the information provided by Kazuhiro
......@@ -761,10 +770,13 @@ public class RestAnnotationParser {
for (String string2 : string) {
boolean remove = false;
for (NoteField field : NoteField.values()) {
if (string2.startsWith(field.getCommonName() + ":") && field.getClazz().isAssignableFrom(object.getClass())) {
remove = true;
} else if (string2.startsWith(field.getCommonName() + "=")
&& field.getClazz().isAssignableFrom(object.getClass())) {
remove = true;
}
}
if (remove) {
annotations.append(string2 + "\n");
......@@ -775,7 +787,10 @@ public class RestAnnotationParser {
String ann = annotations.toString();
setZIndex(object, ann);
if (object instanceof LayerText) {
((LayerText) object).setNotes(newNotes.toString().trim());
LayerText layerText = (LayerText) object;
setBackgroundColor(layerText, ann);
setBorderColor(layerText, ann);
layerText.setNotes(newNotes.toString().trim());
}
if (object instanceof BioEntity) {
BioEntity bioEntity = (BioEntity) object;
......@@ -814,6 +829,47 @@ public class RestAnnotationParser {
}
}
private void setBackgroundColor(LayerText layerText, String ann) {
Color color = getColor(ann, NoteField.BACKGROUND_COLOR.getCommonName());
if (color == null) {
return;
}
if (layerText.getBackgroundColor() == null || layerText.getBackgroundColor().equals(Color.LIGHT_GRAY)) {
layerText.setBackgroundColor(color);
} else if (!layerText.getBackgroundColor().equals(color)) {
logger.warn(new LogMarker(ProjectLogEntryType.PARSING_ISSUE, layerText),
" New background color different than default [" + color + "]["
+ layerText.getBackgroundColor() + "]. Ignoring.");
}
}
private void setBorderColor(LayerText layerText, String ann) {
Color color = getColor(ann, NoteField.BORDER_COLOR.getCommonName());
if (color == null) {
return;
}
if (layerText.getBorderColor() == null || layerText.getBorderColor().equals(Color.LIGHT_GRAY)) {
layerText.setBorderColor(color);
} else if (!layerText.getBorderColor().equals(color)) {
logger.warn(new LogMarker(ProjectLogEntryType.PARSING_ISSUE, layerText),
" New border color different than default [" + color + "]["
+ layerText.getBorderColor() + "]. Ignoring.");
}
}
Color getColor(String notes, String string) {
String lines[] = notes.split("[\n\r]+");
for (String line : lines) {
if (line.startsWith(string + "=") || line.startsWith(string + ":")) {
String colorString = line.replace(string + "=", "").replace(string + ":", "");
return new ColorParser().parse(colorString);
}
}
return null;
}
/**
* Process RDF description from notes, removes it from the description and adds
* appropriate information to miriam data set.
......
......@@ -10,6 +10,7 @@ import lcsb.mapviewer.common.XmlParser;
import lcsb.mapviewer.common.exception.InvalidXmlSchemaException;
import lcsb.mapviewer.converter.annotation.XmlAnnotationParser;
import lcsb.mapviewer.converter.model.celldesigner.CellDesignerElementCollection;
import lcsb.mapviewer.converter.model.celldesigner.CommonXmlParser;
import lcsb.mapviewer.converter.model.celldesigner.annotation.RestAnnotationParser;
import lcsb.mapviewer.converter.model.celldesigner.species.AbstractElementXmlParser;
import lcsb.mapviewer.converter.model.celldesigner.structure.CellDesignerCompartment;
......@@ -34,7 +35,7 @@ public class CompartmentXmlParser extends AbstractElementXmlParser<CellDesignerC
/**
* Parser used to retrieve Miriam data for the element.
*/
private XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser();
private XmlAnnotationParser xmlAnnotationParser;
/**
* Collection of {@link CellDesignerElement cell designer elements} parsed from
......@@ -56,6 +57,7 @@ public class CompartmentXmlParser extends AbstractElementXmlParser<CellDesignerC
*/
public CompartmentXmlParser(CellDesignerElementCollection elements) {
this.elements = elements;
xmlAnnotationParser = new XmlAnnotationParser(CommonXmlParser.RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER);
}
@Override
......
......@@ -80,7 +80,7 @@ public class KineticsXmlParser {
try {
StringBuilder result = new StringBuilder();
result.append("<kineticLaw>");
Node mathNode = XmlParser.getXmlDocumentFromString(kinetics.getDefinition()).getFirstChild();
Node mathNode = XmlParser.getXmlDocumentFromString(kinetics.getDefinition()).getDocumentElement();
for (Node ciNode : XmlParser.getAllNotNecessirellyDirectChild("ci", mathNode)) {
String id = XmlParser.getNodeValue(ciNode).trim();
Element element = model.getElementByElementId(id);
......
......@@ -12,6 +12,7 @@ import lcsb.mapviewer.common.XmlParser;
import lcsb.mapviewer.common.exception.*;
import lcsb.mapviewer.converter.annotation.XmlAnnotationParser;
import lcsb.mapviewer.converter.model.celldesigner.CellDesignerElementCollection;
import lcsb.mapviewer.converter.model.celldesigner.CommonXmlParser;
import lcsb.mapviewer.converter.model.celldesigner.annotation.RestAnnotationParser;
import lcsb.mapviewer.converter.model.celldesigner.geometry.CellDesignerAliasConverter;
import lcsb.mapviewer.converter.model.celldesigner.geometry.ReactionCellDesignerConverter;
......@@ -311,7 +312,7 @@ public class ReactionFromXml {
result = parseReactionExtension(result, node, model);
} else if (node.getNodeName().equalsIgnoreCase("rdf:RDF")) {
try {
XmlAnnotationParser xmlParser = new XmlAnnotationParser();
XmlAnnotationParser xmlParser = new XmlAnnotationParser(CommonXmlParser.RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER);
result.addMiriamData(xmlParser.parseRdfNode(node));
} catch (InvalidXmlSchemaException e) {
throw new ReactionParserException("Problem with parsing RDF", result, e);
......
......@@ -14,6 +14,7 @@ import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.converter.ConverterException;
import lcsb.mapviewer.converter.annotation.XmlAnnotationParser;
import lcsb.mapviewer.converter.model.celldesigner.CellDesignerElementCollection;
import lcsb.mapviewer.converter.model.celldesigner.CommonXmlParser;
import lcsb.mapviewer.converter.model.celldesigner.geometry.CellDesignerAliasConverter;
import lcsb.mapviewer.converter.model.celldesigner.geometry.helper.*;
import lcsb.mapviewer.converter.model.celldesigner.structure.CellDesignerElement;
......@@ -494,7 +495,7 @@ public class ReactionToXml {
sb.append(getLineXmlStringForLines(reaction.getLine()));
XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser();
XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser(CommonXmlParser.RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER);
sb.append("</celldesigner:extension>\n");
sb.append(xmlAnnotationParser.dataSetToXmlString(reaction.getMiriamData(), reaction.getIdReaction()));
sb.append("</annotation>\n");
......
......@@ -13,6 +13,7 @@ import lcsb.mapviewer.common.exception.InvalidXmlSchemaException;
import lcsb.mapviewer.common.exception.NotImplementedException;
import lcsb.mapviewer.converter.annotation.XmlAnnotationParser;
import lcsb.mapviewer.converter.model.celldesigner.CellDesignerElementCollection;
import lcsb.mapviewer.converter.model.celldesigner.CommonXmlParser;
import lcsb.mapviewer.converter.model.celldesigner.annotation.RestAnnotationParser;
import lcsb.mapviewer.converter.model.celldesigner.structure.*;
import lcsb.mapviewer.model.map.InconsistentModelException;
......@@ -67,7 +68,7 @@ public class SpeciesCollectionXmlParser {
/**
* Parser used for parsing annotation.
*/
private XmlAnnotationParser xap = new XmlAnnotationParser();
private XmlAnnotationParser xap;
/**
* Helper class used to decode/encode CellDesigner strings.
......@@ -114,6 +115,7 @@ public class SpeciesCollectionXmlParser {
geneParser = new GeneXmlParser(elements);
proteinParser = new ProteinXmlParser(elements);
antisenseRnaParser = new AntisenseRnaXmlParser(elements);
xap = new XmlAnnotationParser(CommonXmlParser.RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER);
}
/**
......
......@@ -12,6 +12,7 @@ import lcsb.mapviewer.common.XmlParser;
import lcsb.mapviewer.common.exception.*;
import lcsb.mapviewer.converter.annotation.XmlAnnotationParser;
import lcsb.mapviewer.converter.model.celldesigner.CellDesignerElementCollection;
import lcsb.mapviewer.converter.model.celldesigner.CommonXmlParser;
import lcsb.mapviewer.converter.model.celldesigner.annotation.RestAnnotationParser;
import lcsb.mapviewer.converter.model.celldesigner.structure.*;
import lcsb.mapviewer.converter.model.celldesigner.structure.fields.CellDesignerModificationResidue;
......@@ -195,7 +196,7 @@ public class SpeciesSbmlParser extends AbstractElementXmlParser<CellDesignerSpec
}
if (rdfNode != null) {
XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser();
XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser(CommonXmlParser.RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER);
species.addMiriamData(xmlAnnotationParser.parseRdfNode(rdfNode));
}
......@@ -286,7 +287,7 @@ public class SpeciesSbmlParser extends AbstractElementXmlParser<CellDesignerSpec
builder.append("</celldesigner:extension>\n");
XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser();
XmlAnnotationParser xmlAnnotationParser = new XmlAnnotationParser(CommonXmlParser.RELATION_TYPES_SUPPORTED_BY_CELL_DESIGNER);
builder.append(xmlAnnotationParser.dataSetToXmlString(species.getMiriamData(), elements.getElementId(species)));
builder.append("</annotation>\n");
......
......@@ -279,24 +279,6 @@ public class LayerXmlParserTest extends CellDesignerTestFunctions {
assertNotNull(xml);
}
@Test
public void testExtractBackgroundColor() throws Exception {
Color color = parser.extractBackgroundColor("dcxvxcvxcvx\nBackgroundColor=#ccffcc");
assertNotNull(color);
}
@Test
public void testExtractBackgroundColorWithColon() throws Exception {
Color color = parser.extractBackgroundColor("dcxvxcvxcvx\nBackgroundColor:#ccffcc");
assertNotNull(color);
}
@Test
public void testExtractNonExistingBackgroundColor() throws Exception {
Color color = parser.extractBackgroundColor("dcxvxcvxcvx\nBackgroundCffcc");
assertNull(color);
}
@Test
public void testRemoveBackgroundColor() throws Exception {
String notes = parser.removeBackgroundColor("dcxvxcvxcvx\nBackgroundColor=#ccffcc");
......
......@@ -45,7 +45,7 @@ public class SbmlValidationTests extends CellDesignerTestFunctions {
result.add(new Object[] { "testFiles/model_with_annotations.xml" });
result.add(new Object[] { "testFiles/reactions/modifier_with_operator.xml" });
result.add(new Object[] { "testFiles/problematic/problematic_reaction_name.xml" });
result.add(new Object[] { "testFiles/reactions/kinetics.xml" });
return result;
}
......
......@@ -2,6 +2,7 @@ package lcsb.mapviewer.converter.model.celldesigner.annotation;
import static org.junit.Assert.*;
import java.awt.Color;
import java.util.List;
import java.util.Set;
......@@ -18,6 +19,7 @@ import lcsb.mapviewer.common.exception.NotImplementedException;
import lcsb.mapviewer.converter.model.celldesigner.CellDesignerTestFunctions;
import lcsb.mapviewer.converter.model.celldesigner.structure.CellDesignerElement;
import lcsb.mapviewer.model.map.*;
import lcsb.mapviewer.model.map.layout.graphics.LayerText;
import lcsb.mapviewer.model.map.model.Model;
import lcsb.mapviewer.model.map.reaction.Reaction;
import lcsb.mapviewer.model.map.species.*;
......@@ -333,4 +335,27 @@ public class RestAnnotationParserTest extends CellDesignerTestFunctions {
assertFalse(protein.getNotes().contains("Description"));
}
@Test
public void testExtractBackgroundColor() throws Exception {
RestAnnotationParser parser = new RestAnnotationParser();
Color color = parser.getColor("dcxvxcvxcvx\nBackgroundColor=#ccffcc", NoteField.BACKGROUND_COLOR.getCommonName());
assertNotNull(color);
}
@Test
public void testExtractBackgroundColorWithColon() throws Exception {
RestAnnotationParser parser = new RestAnnotationParser();
Color color = parser.getColor("dcxvxcvxcvx\nBackgroundColor:#ccffcc", NoteField.BACKGROUND_COLOR.getCommonName());
assertNotNull(color);
}
@Test
public void testExtractNonExistingBackgroundColor() throws Exception {
RestAnnotationParser parser = new RestAnnotationParser();
Color color = parser.getColor("dcxvxcvxcvx\nBackgroundCffcc", NoteField.BACKGROUND_COLOR.getCommonName());
assertNull(color);
}
}