-/**\r
-* Copyright (C) 2007 EDIT\r
-* European Distributed Institute of Taxonomy \r
-* http://www.e-taxonomy.eu\r
-* \r
-* The contents of this file are subject to the Mozilla Public License Version 1.1\r
-* See LICENSE.TXT at the top of this package for the full license terms.\r
-*/\r
-\r
-package eu.etaxonomy.cdm.io.markup;\r
-\r
-import java.net.MalformedURLException;\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.Stack;\r
-import java.util.UUID;\r
-import java.util.regex.Matcher;\r
-import java.util.regex.Pattern;\r
-\r
-import javax.xml.namespace.QName;\r
-import javax.xml.stream.Location;\r
-import javax.xml.stream.XMLEventReader;\r
-import javax.xml.stream.XMLStreamConstants;\r
-import javax.xml.stream.XMLStreamException;\r
-import javax.xml.stream.events.Attribute;\r
-import javax.xml.stream.events.Characters;\r
-import javax.xml.stream.events.EndElement;\r
-import javax.xml.stream.events.StartElement;\r
-import javax.xml.stream.events.XMLEvent;\r
-\r
-import org.apache.commons.lang.StringUtils;\r
-import org.apache.commons.lang.WordUtils;\r
-import org.apache.log4j.Logger;\r
-\r
-import eu.etaxonomy.cdm.api.service.IClassificationService;\r
-import eu.etaxonomy.cdm.api.service.ITermService;\r
-import eu.etaxonomy.cdm.common.CdmUtils;\r
-import eu.etaxonomy.cdm.ext.geo.GeoServiceArea;\r
-import eu.etaxonomy.cdm.ext.geo.IEditGeoService;\r
-import eu.etaxonomy.cdm.io.common.CdmImportBase;\r
-import eu.etaxonomy.cdm.io.common.CdmImportBase.TermMatchMode;\r
-import eu.etaxonomy.cdm.io.common.events.IIoEvent;\r
-import eu.etaxonomy.cdm.io.common.events.IoProblemEvent;\r
-import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;\r
-import eu.etaxonomy.cdm.model.agent.Team;\r
-import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;\r
-import eu.etaxonomy.cdm.model.common.AnnotatableEntity;\r
-import eu.etaxonomy.cdm.model.common.Annotation;\r
-import eu.etaxonomy.cdm.model.common.AnnotationType;\r
-import eu.etaxonomy.cdm.model.common.CdmBase;\r
-import eu.etaxonomy.cdm.model.common.DefinedTermBase;\r
-import eu.etaxonomy.cdm.model.common.Extension;\r
-import eu.etaxonomy.cdm.model.common.ExtensionType;\r
-import eu.etaxonomy.cdm.model.common.Language;\r
-import eu.etaxonomy.cdm.model.common.OriginalSourceType;\r
-import eu.etaxonomy.cdm.model.common.TermVocabulary;\r
-import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
-import eu.etaxonomy.cdm.model.description.Distribution;\r
-import eu.etaxonomy.cdm.model.description.Feature;\r
-import eu.etaxonomy.cdm.model.description.PolytomousKey;\r
-import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;\r
-import eu.etaxonomy.cdm.model.description.PresenceTerm;\r
-import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
-import eu.etaxonomy.cdm.model.description.TextData;\r
-import eu.etaxonomy.cdm.model.location.NamedArea;\r
-import eu.etaxonomy.cdm.model.location.NamedAreaLevel;\r
-import eu.etaxonomy.cdm.model.location.NamedAreaType;\r
-import eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity;\r
-import eu.etaxonomy.cdm.model.media.Media;\r
-import eu.etaxonomy.cdm.model.name.NomenclaturalCode;\r
-import eu.etaxonomy.cdm.model.name.NonViralName;\r
-import eu.etaxonomy.cdm.model.name.Rank;\r
-import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;\r
-import eu.etaxonomy.cdm.model.reference.Reference;\r
-import eu.etaxonomy.cdm.model.taxon.Classification;\r
-import eu.etaxonomy.cdm.model.taxon.Taxon;\r
-import eu.etaxonomy.cdm.model.taxon.TaxonBase;\r
-import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;\r
-\r
-/**\r
- * @author a.mueller\r
- * @created 04.08.2008\r
- */\r
-public abstract class MarkupImportBase {\r
- @SuppressWarnings("unused")\r
- private static final Logger logger = Logger.getLogger(MarkupImportBase.class);\r
-\r
- //Base\r
- protected static final String ALTITUDE = "altitude";\r
- protected static final String ANNOTATION = "annotation";\r
- protected static final String BOLD = "bold";\r
- protected static final String BR = "br";\r
- protected static final String CITATION = "citation";\r
- protected static final String CLASS = "class";\r
- protected static final String COORDINATES = "coordinates";\r
- protected static final String DATES = "dates";\r
- protected static final String GATHERING = "gathering";\r
- protected static final String GENUS_ABBREVIATION = "genus abbreviation";\r
- protected static final String FOOTNOTE = "footnote";\r
- protected static final String FOOTNOTE_REF = "footnoteRef";\r
- protected static final String FULL_NAME = "fullName";\r
- protected static final String ITALICS = "italics";\r
- protected static final String NUM = "num";\r
- protected static final String NOTES = "notes";\r
- protected static final String PUBLICATION = "publication";\r
- protected static final String SPECIMEN_TYPE = "specimenType";\r
- protected static final String STATUS = "status";\r
- protected static final String SUB_HEADING = "subHeading";\r
- protected static final String TYPE = "type";\r
- protected static final String TYPE_STATUS = "typeStatus";\r
-\r
- protected static final boolean CREATE_NEW = true;\r
- protected static final boolean NO_IMAGE_GALLERY = false;\r
- protected static final boolean IMAGE_GALLERY = true;\r
-\r
- protected static final String ADDENDA = "addenda";\r
- protected static final String BIBLIOGRAPHY = "bibliography";\r
- protected static final String BIOGRAPHIES = "biographies";\r
- protected static final String CHAR = "char";\r
- protected static final String DEDICATION = "dedication";\r
- protected static final String DEFAULT_MEDIA_URL = "defaultMediaUrl";\r
- protected static final String DISTRIBUTION_LIST = "distributionList";\r
- protected static final String DISTRIBUTION_LOCALITY = "distributionLocality";\r
- protected static final String FEATURE = "feature";\r
- protected static final String FIGURE = "figure";\r
- protected static final String FIGURE_LEGEND = "figureLegend";\r
- protected static final String FIGURE_PART = "figurePart";\r
- protected static final String FIGURE_REF = "figureRef";\r
- protected static final String FIGURE_TITLE = "figureTitle";\r
- protected static final String FOOTNOTE_STRING = "footnoteString";\r
- protected static final String FREQUENCY = "frequency";\r
- protected static final String HEADING = "heading";\r
- protected static final String HABITAT = "habitat";\r
- protected static final String HABITAT_LIST = "habitatList";\r
- protected static final String IS_FREETEXT = "isFreetext";\r
- protected static final String ID = "id";\r
- protected static final String KEY = "key";\r
- protected static final String LIFE_CYCLE_PERIODS = "lifeCyclePeriods";\r
- protected static final String META_DATA = "metaData";\r
- protected static final String MODS = "mods";\r
-\r
- protected static final String NOMENCLATURE = "nomenclature";\r
- protected static final String QUOTE = "quote";\r
- protected static final String RANK = "rank";\r
- protected static final String REF = "ref";\r
- protected static final String REF_NUM = "refNum";\r
- protected static final String REFERENCE = "reference";\r
- protected static final String REFERENCES = "references";\r
- protected static final String SUB_CHAR = "subChar";\r
- protected static final String TAXON = "taxon";\r
- protected static final String TAXONTITLE = "taxontitle";\r
- protected static final String TAXONTYPE = "taxontype";\r
- protected static final String TEXT_SECTION = "textSection";\r
- protected static final String TREATMENT = "treatment";\r
- protected static final String SERIALS_ABBREVIATIONS = "serialsAbbreviations";\r
- protected static final String STRING = "string";\r
- protected static final String URL = "url";\r
- protected static final String VERNACULAR_NAMES = "vernacularNames";\r
- protected static final String WRITER = "writer";\r
- \r
- \r
- //Nomenclature\r
- protected static final String ACCEPTED = "accepted";\r
- protected static final String ACCEPTED_NAME = "acceptedName";\r
- protected static final String ALTERNATEPUBTITLE = "alternatepubtitle";\r
- protected static final String AUTHOR = "author";\r
- protected static final String DETAILS = "details";\r
- protected static final String EDITION = "edition";\r
- protected static final String EDITORS = "editors";\r
- protected static final String HOMONYM = "homonym";\r
- protected static final String HOMOTYPES = "homotypes";\r
- protected static final String INFRANK = "infrank";\r
- protected static final String INFRAUT = "infraut";\r
- protected static final String INFRPARAUT = "infrparaut";\r
- protected static final String ISSUE = "issue";\r
- protected static final String NAME = "name";\r
- protected static final String NAME_TYPE = "nameType";\r
- protected static final String NOM = "nom";\r
- protected static final String PAGES = "pages";\r
- protected static final String PARAUT = "paraut";\r
- protected static final String PUBFULLNAME = "pubfullname";\r
- protected static final String PUBNAME = "pubname";\r
- protected static final String PUBTITLE = "pubtitle";\r
- protected static final String PUBTYPE = "pubtype";\r
- protected static final String REF_PART = "refPart";\r
- protected static final String SYNONYM = "synonym";\r
- protected static final String USAGE = "usage";\r
- protected static final String VOLUME = "volume";\r
- protected static final String YEAR = "year";\r
-\r
- \r
- //keys\r
- protected static final String COUPLET = "couplet";\r
- protected static final String IS_SPOTCHARACTERS = "isSpotcharacters";\r
- protected static final String ONLY_NUMBERED_TAXA_EXIST = "onlyNumberedTaxaExist";\r
- protected static final String EXISTS = "exists";\r
- protected static final String KEYNOTES = "keynotes";\r
- protected static final String KEY_TITLE = "keyTitle";\r
- protected static final String QUESTION = "question";\r
- protected static final String TEXT = "text";\r
- protected static final String TO_COUPLET = "toCouplet";\r
- protected static final String TO_KEY = "toKey";\r
- protected static final String TO_TAXON = "toTaxon";\r
-\r
-\r
- protected MarkupDocumentImport docImport;\r
-\r
- private IEditGeoService editGeoService;\r
- \r
- public MarkupImportBase(MarkupDocumentImport docImport) {\r
- super();\r
- this.docImport = docImport;\r
- this.editGeoService = docImport.getEditGeoService();\r
- }\r
-\r
- private Stack<QName> unhandledElements = new Stack<QName>();\r
- private Stack<QName> handledElements = new Stack<QName>();\r
-\r
-\r
- protected <T extends CdmBase> void save(Collection<T> collection, MarkupImportState state) {\r
- if (state.isCheck() || collection.isEmpty()){\r
- return;\r
- }\r
- T example = collection.iterator().next();\r
- if (example.isInstanceOf(TaxonBase.class)){\r
- Collection<TaxonBase> typedCollection = (Collection<TaxonBase>)collection;\r
- docImport.getTaxonService().saveOrUpdate(typedCollection);\r
- }else if (example.isInstanceOf(Classification.class)){\r
- Collection<Classification> typedCollection = (Collection<Classification>)collection;\r
- docImport.getClassificationService().saveOrUpdate(typedCollection);\r
- }else if (example.isInstanceOf(PolytomousKey.class)){\r
- Collection<PolytomousKey> typedCollection = (Collection<PolytomousKey>)collection;\r
- docImport.getPolytomousKeyService().saveOrUpdate(typedCollection);\r
- }else if (example.isInstanceOf(DefinedTermBase.class)){\r
- Collection<DefinedTermBase> typedCollection = (Collection<DefinedTermBase>)collection;\r
- getTermService().saveOrUpdate(typedCollection);\r
- }\r
- \r
- }\r
- \r
-\r
- //TODO move to service layer for all IdentifiableEntities \r
- protected void save(CdmBase cdmBase, MarkupImportState state) {\r
- if (state.isCheck()){\r
- return;\r
- }\r
- cdmBase = CdmBase.deproxy(cdmBase, CdmBase.class);\r
- if (cdmBase == null){\r
- String message = "Tried to save a null object.";\r
- fireWarningEvent(message, "--location ?? --", 6,1);\r
- } else if (cdmBase.isInstanceOf(TaxonBase.class)){\r
- docImport.getTaxonService().saveOrUpdate((TaxonBase<?>)cdmBase);\r
- }else if (cdmBase.isInstanceOf(Classification.class)){\r
- docImport.getClassificationService().saveOrUpdate((Classification)cdmBase);\r
- }else if (cdmBase.isInstanceOf(PolytomousKey.class)){\r
- docImport.getPolytomousKeyService().saveOrUpdate((PolytomousKey)cdmBase);\r
- }else if (cdmBase.isInstanceOf(DefinedTermBase.class)){\r
- docImport.getTermService().saveOrUpdate((DefinedTermBase<?>)cdmBase);\r
- }else if (cdmBase.isInstanceOf(Media.class)){\r
- docImport.getMediaService().saveOrUpdate((Media)cdmBase);\r
- }else if (cdmBase.isInstanceOf(SpecimenOrObservationBase.class)){\r
- docImport.getOccurrenceService().saveOrUpdate((SpecimenOrObservationBase<?>)cdmBase);\r
- }else if (cdmBase.isInstanceOf(DescriptionElementBase.class)){\r
- docImport.getDescriptionService().saveDescriptionElement((DescriptionElementBase)cdmBase);\r
- }else if (cdmBase.isInstanceOf(Reference.class)){\r
- docImport.getReferenceService().saveOrUpdate((Reference<?>)cdmBase);\r
- }else{\r
- String message = "Unknown cdmBase type to save: " + cdmBase.getClass();\r
- fireWarningEvent(message, "Unknown location", 8);\r
- }\r
- //logger.warn("Saved " + cdmBase);\r
- }\r
- \r
- \r
- protected ITermService getTermService() {\r
- return docImport.getTermService();\r
- }\r
- \r
- protected IClassificationService getClassificationService() {\r
- return docImport.getClassificationService();\r
- }\r
-\r
-//*********************** Attribute methods *************************************/\r
-\r
- /**\r
- * Returns a map for all attributes of an start element\r
- * @param event\r
- * @return\r
- */\r
- protected Map<String, Attribute> getAttributes(XMLEvent event) {\r
- Map<String, Attribute> result = new HashMap<String, Attribute>();\r
- if (!event.isStartElement()){\r
- fireWarningEvent("Event is not an startElement. Can't check attributes", makeLocationStr(event.getLocation()), 1, 1);\r
- return result;\r
- }\r
- StartElement element = event.asStartElement(); \r
- Iterator<Attribute> attributes = element.getAttributes();\r
- while (attributes.hasNext()){\r
- Attribute attribute = attributes.next();\r
- //TODO namespaces\r
- result.put(attribute.getName().getLocalPart(), attribute);\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * Throws an unexpected attributes event if the event has any attributes.\r
- * @param event\r
- */\r
- protected void checkNoAttributes(Map<String, Attribute> attributes, XMLEvent event) {\r
- String[] exceptions = new String[]{};\r
- handleUnexpectedAttributes(event.getLocation(), attributes, 1, exceptions);\r
- }\r
- \r
- \r
- \r
- /**\r
- * Throws an unexpected attributes event if the event has any attributes.\r
- * @param event\r
- */\r
- protected void checkNoAttributes(XMLEvent event) {\r
- String[] exceptions = new String[]{};\r
- checkNoAttributes(event, 1, exceptions); \r
- }\r
-\r
- /**\r
- * Throws an unexpected attributes event if the event has any attributes except those mentioned in "exceptions".\r
- * @param event\r
- * @param exceptions\r
- */\r
- protected void checkNoAttributes(XMLEvent event, int stackDepth, String... exceptions) {\r
- if (! event.isStartElement()){\r
- fireWarningEvent("Event is not an startElement. Can't check attributes", makeLocationStr(event.getLocation()), 1, 1);\r
- return;\r
- }\r
- StartElement startElement = event.asStartElement();\r
- Map<String, Attribute> attributes = getAttributes(startElement);\r
- handleUnexpectedAttributes(startElement.getLocation(), attributes, stackDepth+1, exceptions);\r
- }\r
- \r
-\r
- /**\r
- * Checks if the given attribute exists and has the given value.\r
- * If yes, true is returned and the attribute is removed from the attributes map.\r
- * Otherwise false is returned.\r
- * @param attributes\r
- * @param attrName\r
- * @param value\r
- * @return <code>true</code> if attribute has given value, <code>false</code> otherwise\r
- */\r
- protected boolean checkAndRemoveAttributeValue( Map<String, Attribute> attributes, String attrName, String value) {\r
- Attribute attr = attributes.get(attrName);\r
- if (attr == null ||value == null ){\r
- return false;\r
- }else{\r
- if (value.equals(attr.getValue())){\r
- attributes.remove(attrName);\r
- return true;\r
- }else{\r
- return false;\r
- }\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Returns the value of a given attribute name and removes the attribute from the attributes map. \r
- * @param attributes\r
- * @param attrName\r
- * @return\r
- */\r
- protected String getAndRemoveAttributeValue(Map<String, Attribute> attributes, String attrName) {\r
- return getAndRemoveAttributeValue(null, attributes, attrName, false, 1);\r
- }\r
- \r
- /**\r
- * Returns the value of a boolean attribute with the given name and removes the attribute from the attributes map. \r
- * Returns <code>defaultValue</code> if the attribute does not exist. ALso returns <code>defaultValue</code> and throws a warning if the\r
- * attribute has no boolean value (true, false).\r
- * @param \r
- * @param attributes the \r
- * @param attrName the name of the attribute\r
- * @param defaultValue the default value to return if attribute does not exist or can not be defined\r
- * @return\r
- */\r
- protected Boolean getAndRemoveBooleanAttributeValue(XMLEvent event, Map<String, Attribute> attributes, String attrName, Boolean defaultValue) {\r
- String value = getAndRemoveAttributeValue(null, attributes, attrName, false, 1);\r
- Boolean result = defaultValue;\r
- if (value != null){\r
- if (value.equalsIgnoreCase("true")){\r
- result = true;\r
- }else if (value.equalsIgnoreCase("false")){\r
- result = false;\r
- }else{\r
- String message = "Boolean attribute has no boolean value ('true', 'false') but '%s'";\r
- fireWarningEvent(String.format(message, value), makeLocationStr(event.getLocation()), 6, 1);\r
- }\r
- }\r
- return result;\r
- }\r
-\r
- \r
- /**\r
- * Returns the value of a given attribute name and returns the attribute from the attributes map.\r
- * Fires a mandatory field is missing event if the attribute does not exist.\r
- * @param xmlEvent\r
- * @param attributes\r
- * @param attrName\r
- * @return\r
- */\r
- protected String getAndRemoveRequiredAttributeValue(XMLEvent xmlEvent, Map<String, Attribute> attributes, String attrName) {\r
- return getAndRemoveAttributeValue(xmlEvent, attributes, attrName, true, 1);\r
- }\r
- \r
- /**\r
- * Returns the value of a given attribute name and returns the attribute from the attributes map.\r
- * If required is <code>true</code> and the attribute does not exist a mandatory field is missing event is fired.\r
- * @param xmlEvent\r
- * @param attributes\r
- * @param attrName\r
- * @param isRequired\r
- * @return\r
- */\r
- private String getAndRemoveAttributeValue(XMLEvent xmlEvent, Map<String, Attribute> attributes, String attrName, boolean isRequired, int stackDepth) {\r
- Attribute attr = attributes.get(attrName);\r
- if (attr == null ){\r
- if (isRequired){\r
- fireMandatoryElementIsMissing(xmlEvent, attrName, 8, stackDepth+1);\r
- }\r
- return null;\r
- }else{\r
- attributes.remove(attrName);\r
- return attr.getValue();\r
- }\r
- } \r
-\r
- /**\r
- * Fires an not yet implemented event if the given attribute exists in attributes.\r
- * @param attributes\r
- * @param attrName\r
- */\r
- protected void handleNotYetImplementedAttribute(Map<String, Attribute> attributes, String attrName) {\r
- Attribute attr = attributes.get(attrName);\r
- if (attr != null){\r
- attributes.remove(attrName);\r
- QName qName = attr.getName();\r
- fireNotYetImplementedAttribute(attr.getLocation(), qName, 1);\r
- }\r
- }\r
-\r
- /**\r
- * Fires an unhandled attributes event, if attributes exist in attributes map not covered by the exceptions.\r
- * No event is fired if the unhandled elements stack is not empty.\r
- * @param location\r
- * @param attributes\r
- * @param exceptions\r
- */\r
- protected void handleUnexpectedAttributes(Location location,Map<String, Attribute> attributes, String... exceptions) {\r
- handleUnexpectedAttributes(location, attributes, 1, exceptions);\r
- }\r
- \r
- /**\r
- * see {@link #handleUnexpectedAttributes(Location, Map, String...)}\r
- *\r
- * @param location\r
- * @param attributes\r
- * @param stackDepth the stack trace depth\r
- * @param exceptions\r
- */\r
- private void handleUnexpectedAttributes(Location location,Map<String, Attribute> attributes, int stackDepth, String... exceptions) {\r
- if (attributes.size() > 0){\r
- if (this.unhandledElements.size() == 0 ){\r
- boolean hasUnhandledAttributes = false;\r
- for (String key : attributes.keySet()){\r
- boolean isException = false;\r
- for (String exception : exceptions){\r
- if(key.equals(exception)){\r
- isException = true;\r
- }\r
- }\r
- if (!isException){\r
- hasUnhandledAttributes = true;\r
- }\r
- }\r
- if (hasUnhandledAttributes){\r
- fireUnexpectedAttributes(location, attributes, stackDepth+1);\r
- }\r
- }\r
- }\r
- }\r
-\r
- \r
- private void fireUnexpectedAttributes(Location location, Map<String, Attribute> attributes, int stackDepth) {\r
- String attributesString = "";\r
- for (String key : attributes.keySet()){\r
- Attribute attribute = attributes.get(key);\r
- attributesString = CdmUtils.concat(",", attributesString, attribute.getName().getLocalPart() + ":" + attribute.getValue());\r
- }\r
- String message = "Unexpected attributes: %s";\r
- IoProblemEvent event = makeProblemEvent(location, String.format(message, attributesString), 1 , stackDepth +1 );\r
- fire(event); \r
- }\r
- \r
-\r
- protected void fireUnexpectedAttributeValue(XMLEvent parentEvent, String attrName, String attrValue) {\r
- String message = "Unexpected attribute value %s='%s'";\r
- message = String.format(message, attrName, attrValue);\r
- IoProblemEvent event = makeProblemEvent(parentEvent.getLocation(), message, 1 , 1 );\r
- fire(event);\r
- }\r
-\r
- protected void handleNotYetImplementedAttributeValue(XMLEvent xmlEvent, String attrName, String attrValue) {\r
- String message = "Attribute %s not yet implemented for value '%s'";\r
- message = String.format(message, attrName, attrValue);\r
- IIoEvent event = makeProblemEvent(xmlEvent.getLocation(), message, 1, 1 );\r
- fire(event); \r
- }\r
- \r
- protected void fireNotYetImplementedAttribute(Location location, QName qName, int stackDepth) {\r
- String message = "Attribute not yet implemented: %s";\r
- IIoEvent event = makeProblemEvent(location, String.format(message, qName.getLocalPart()), 1, stackDepth+1 );\r
- fire(event); \r
- }\r
- \r
-\r
- protected void fireUnexpectedEvent(XMLEvent xmlEvent, int stackDepth) {\r
- Location location = xmlEvent.getLocation();\r
- String message = "Unexpected event: %s";\r
- IIoEvent event = makeProblemEvent(location, String.format(message, xmlEvent.toString()), 2, stackDepth +1);\r
- fire(event); \r
- }\r
-\r
- protected void fireUnexpectedStartElement(Location location, StartElement startElement, int stackDepth) {\r
- QName qName = startElement.getName();\r
- String message = "Unexpected start element: %s";\r
- IIoEvent event = makeProblemEvent(location, String.format(message, qName.getLocalPart()), 2, stackDepth +1);\r
- fire(event); \r
- }\r
- \r
-\r
- protected void fireUnexpectedEndElement(Location location, EndElement endElement, int stackDepth) {\r
- QName qName = endElement.getName();\r
- String message = "Unexpected end element: %s";\r
- IIoEvent event = makeProblemEvent(location, String.format(message, qName.getLocalPart()), 16, stackDepth+1);\r
- fire(event); \r
- }\r
- \r
- protected void fireNotYetImplementedElement(Location location, QName qName, int stackDepth) {\r
- String message = "Element not yet implemented: %s";\r
- IIoEvent event = makeProblemEvent(location, String.format(message, qName.getLocalPart()), 1, stackDepth+1 );\r
- fire(event); \r
- }\r
-\r
- protected void fireNotYetImplementedCharacters(Location location, Characters chars, int stackDepth) {\r
- String message = "Characters not yet handled: %s";\r
- IIoEvent event = makeProblemEvent(location, String.format(message, chars.getData()), 1, stackDepth+1 );\r
- fire(event); \r
- }\r
-\r
- /**\r
- * Creates a problem event.\r
- * Be aware of the right depths of the stack trace !\r
- * @param location \r
- * @param message\r
- * @param severity\r
- * @return\r
- */\r
- private IoProblemEvent makeProblemEvent(Location location, String message, int severity, int stackDepth) {\r
- stackDepth++;\r
- StackTraceElement[] stackTrace = new Exception().getStackTrace();\r
- int lineNumber = stackTrace[stackDepth].getLineNumber();\r
- String methodName = stackTrace[stackDepth].getMethodName();\r
- String locationStr = makeLocationStr(location);\r
- String className = stackTrace[stackDepth].getClassName();\r
- Class<?> declaringClass;\r
- try {\r
- declaringClass = Class.forName(className);\r
- } catch (ClassNotFoundException e) {\r
- declaringClass = this.getClass();\r
- }\r
- IoProblemEvent event = IoProblemEvent.NewInstance(declaringClass, message, \r
- locationStr, lineNumber, severity, methodName);\r
- return event;\r
- }\r
-\r
- /**\r
- * Creates a string from a location\r
- * @param location\r
- * @return\r
- */\r
- protected String makeLocationStr(Location location) {\r
- String locationStr = location == null ? " - no location - " : "l." + location.getLineNumber() + "/c."+ location.getColumnNumber();\r
- return locationStr;\r
- }\r
- \r
-\r
- /**\r
- * Fires an unexpected element event if the unhandled elements stack is empty.\r
- * Otherwise adds the element to the stack.\r
- * @param event\r
- */\r
- protected void handleUnexpectedStartElement(XMLEvent event) {\r
- handleUnexpectedStartElement(event, 1);\r
- }\r
- \r
- /**\r
- * Fires an unexpected element event if the unhandled elements stack is empty.\r
- * Otherwise adds the element to the stack.\r
- * @param event\r
- */\r
- protected void handleUnexpectedStartElement(XMLEvent event, int stackDepth) {\r
- QName qName = event.asStartElement().getName();\r
- if (! unhandledElements.empty()){\r
- unhandledElements.push(qName);\r
- }else{\r
- fireUnexpectedStartElement(event.getLocation(), event.asStartElement(), stackDepth + 1);\r
- } \r
- }\r
-\r
- \r
- protected void handleUnexpectedEndElement(EndElement event) {\r
- handleUnexpectedEndElement(event, 1);\r
- }\r
- \r
- /**\r
- * Fires an unexpected element event if the event is not the last on the stack.\r
- * Otherwise removes last stack element.\r
- * @param event\r
- */\r
- protected void handleUnexpectedEndElement(EndElement event, int stackDepth) {\r
- QName qName = event.asEndElement().getName();\r
- if (!unhandledElements.isEmpty() && unhandledElements.peek().equals(qName)){\r
- unhandledElements.pop();\r
- }else{\r
- fireUnexpectedEndElement(event.getLocation(), event.asEndElement(), stackDepth + 1);\r
- }\r
- }\r
- \r
- /**\r
- * \r
- * @param endElement\r
- */\r
- protected void popUnimplemented(EndElement endElement) {\r
- QName qName = endElement.asEndElement().getName();\r
- if (unhandledElements.peek().equals(qName)){\r
- unhandledElements.pop();\r
- }else{\r
- String message = "End element is not last on stack: %s";\r
- message = String.format(message, qName.getLocalPart());\r
- IIoEvent event = makeProblemEvent(endElement.getLocation(), message, 16, 1);\r
- fire(event);\r
- }\r
- \r
- }\r
- \r
- \r
- /**\r
- * Fires an unexpected element event if the unhandled element stack is empty.\r
- * @param event\r
- */\r
- protected void handleUnexpectedElement(XMLEvent event) {\r
- if (event.isStartElement()){\r
- handleUnexpectedStartElement(event);\r
- }else if (event.isEndElement()){\r
- handleUnexpectedEndElement(event.asEndElement());\r
- }else if (event.getEventType() == XMLStreamConstants.COMMENT){\r
- //do nothing\r
- }else if (! unhandledElements.empty()){\r
- //do nothing\r
- }else{\r
- fireUnexpectedEvent(event, 1);\r
- } \r
- }\r
- \r
- /**\r
- * Fires an not yet implemented event and adds the element name to the unhandled elements stack.\r
- * @param event\r
- */\r
- protected void handleNotYetImplementedCharacters(XMLEvent event) {\r
- Characters chars = event.asCharacters();\r
- fireNotYetImplementedCharacters(event.getLocation(), chars, 1);\r
- }\r
-\r
- /**\r
- * Fires an not yet implemented event and adds the element name to the unhandled elements stack.\r
- * @param event\r
- */\r
- protected void handleNotYetImplementedElement(XMLEvent event) {\r
- QName qName = event.asStartElement().getName();\r
- boolean isTopLevel = unhandledElements.isEmpty();\r
- unhandledElements.push(qName);\r
- if (isTopLevel){\r
- fireNotYetImplementedElement(event.getLocation(), qName, 1);\r
- }\r
- }\r
-\r
- /**\r
- * Checks if a mandatory text is not empty or null.\r
- * Returns true if text is given.\r
- * Fires an mandatory element is missing event otherwise and returns <code>null</code>.\r
- * @param text\r
- * @param parentEvent\r
- * @return\r
- */\r
- protected boolean checkMandatoryText(String text, XMLEvent parentEvent) {\r
- if (! StringUtils.isNotBlank(text)){\r
- fireMandatoryElementIsMissing(parentEvent, "CData", 4, 1);\r
- return false;\r
- }\r
- return true;\r
- }\r
- \r
- /**\r
- * Fires an mandatory element is missing event if exists is <code>false</code>.\r
- * @param hasMandatory\r
- * @param parentEvent\r
- * @param string\r
- */\r
- protected void checkMandatoryElement(boolean exists, StartElement parentEvent, String attrName) {\r
- if (! exists){\r
- fireMandatoryElementIsMissing(parentEvent, attrName, 5, 1);\r
- }\r
- }\r
-\r
- \r
- /**\r
- * Fires an element is missing event.\r
- * @param xmlEvent\r
- * @param string\r
- * @param severity\r
- * @param stackDepth\r
- * @throws IllegalStateException if xmlEvent is not a StartElement and not an Attribute\r
- */\r
- private void fireMandatoryElementIsMissing(XMLEvent xmlEvent, String missingEventName, int severity, int stackDepth) throws IllegalStateException{\r
- Location location = xmlEvent.getLocation();\r
- String typeName;\r
- QName qName;\r
- if (xmlEvent.isAttribute()){\r
- Attribute attribute = ((Attribute)xmlEvent);\r
- typeName = "attribute";\r
- qName = attribute.getName();\r
- }else if (xmlEvent.isStartElement()){\r
- typeName = "element";\r
- qName = xmlEvent.asStartElement().getName();\r
- }else{\r
- throw new IllegalStateException("mandatory element only allowed for attributes and start tags in " + makeLocationStr(location));\r
- }\r
- String message = "Mandatory %s '%s' is missing in %s";\r
- message = String.format(message, typeName , missingEventName, qName.getLocalPart());\r
- IIoEvent event = makeProblemEvent(location, message, severity, stackDepth +1);\r
- fire(event); \r
- }\r
- \r
-\r
-\r
-\r
- /**\r
- * Returns true if the "next" event is the ending tag for the "parent" event.\r
- * @param next end element to test, must not be null\r
- * @param parentEvent start element to test\r
- * @return true if the "next" event is the ending tag for the "parent" event.\r
- * @throws XMLStreamException\r
- */\r
- protected boolean isMyEndingElement(XMLEvent next, XMLEvent parentEvent) throws XMLStreamException {\r
- if (! parentEvent.isStartElement()){\r
- String message = "Parent event should be start tag";\r
- fireWarningEvent(message, makeLocationStr(next.getLocation()), 6);\r
- return false;\r
- }\r
- return isEndingElement(next, parentEvent.asStartElement().getName().getLocalPart());\r
- }\r
- \r
- /**\r
- * Trims the text and removes turns all whitespaces into single empty space.\r
- * @param text\r
- * @return\r
- */\r
- protected String normalize(String text) {\r
- text = StringUtils.trimToEmpty(text);\r
- text = text.replaceAll("\\s+", " ");\r
- return text;\r
- }\r
- \r
-\r
-\r
- /**\r
- * Removes whitespaces at beginning and end and makes the first letter\r
- * a capital letter and all other letters small letters.\r
- * @param value\r
- * @return\r
- */\r
- protected String toFirstCapital(String value) {\r
- if (StringUtils.isBlank(value)){\r
- return value;\r
- }else{\r
- String result = "";\r
- value = value.trim();\r
- result += value.trim().substring(0,1).toUpperCase();\r
- if (value.length()>1){\r
- result += value.substring(1).toLowerCase();\r
- }\r
- return result;\r
- }\r
- }\r
- \r
- /**\r
- * Currently not used.\r
- * @param str\r
- * @param allowedNumberOfCharacters\r
- * @param onlyFirstCapital\r
- * @return\r
- */\r
- protected boolean isAbbreviation(String str, int allowedNumberOfCharacters, boolean onlyFirstCapital){\r
- if (isBlank(str)){\r
- return false;\r
- }\r
- str = str.trim();\r
- if (! str.endsWith(".")){\r
- return false;\r
- }\r
- str = str.substring(0, str.length() -1);\r
- if (str.length() > allowedNumberOfCharacters){\r
- return false;\r
- }\r
- final String re = "^\\p{javaUpperCase}\\p{javaLowerCase}*$";\r
- if (str.matches(re)){\r
- return true;\r
- }else{\r
- return false;\r
- }\r
- }\r
- \r
- /**\r
- * Checks if <code>abbrev</code> is the short form for the genus name (strGenusName).\r
- * Usually this is the case if <code>abbrev</code> is the first letter (optional with ".") \r
- * of strGenusName. But in older floras it may also be the first 2 or 3 letters (optional with dot).\r
- * However, we allow only a maximum of 2 letters to be anambigous. In cases with 3 letters better \r
- * change the original markup data.\r
- * @param single\r
- * @param strGenusName\r
- * @return\r
- */\r
- protected boolean isGenusAbbrev(String abbrev, String strGenusName) {\r
- if (! abbrev.matches("[A-Z][a-z]?\\.?")) {\r
- return false;\r
- }else if (abbrev.length() == 0 || strGenusName == null || strGenusName.length() == 0){\r
- return false; \r
- }else{\r
- abbrev = abbrev.replace(".", "");\r
- return strGenusName.startsWith(abbrev);\r
-// boolean result = true;\r
-// for (int i = 0 ; i < abbrev.length(); i++){\r
-// result &= ( abbrev.charAt(i) == strGenusName.charAt(i));\r
-// }\r
-// return result;\r
- }\r
- }\r
-\r
- \r
- /**\r
- * Checks if all words in the given string start with a capital letter but do not have any further capital letter.\r
- * @param word the string to be checekd. Usually should be a single word.\r
- * @return true if the above is the case, false otherwise\r
- */\r
- protected boolean isFirstCapitalWord(String word) {\r
- if (WordUtils.capitalizeFully(word).equals(word)){\r
- return true;\r
- }else if (WordUtils.capitalizeFully(word,new char[]{'-'}).equals(word)){\r
- //for words like Le-Testui (which is a species epithet)\r
- return true;\r
- }else{\r
- return false;\r
- }\r
- }\r
- \r
-\r
- /**\r
- * Read next event. Ignore whitespace events.\r
- * @param reader\r
- * @return\r
- * @throws XMLStreamException\r
- */\r
- protected XMLEvent readNoWhitespace(XMLEventReader reader) throws XMLStreamException {\r
- XMLEvent event = reader.nextEvent();\r
- while (event.isCharacters() && event.asCharacters().isWhiteSpace()){\r
- event = reader.nextEvent();\r
- }\r
- return event;\r
- }\r
- \r
- /**\r
- * Returns the REQUIRED "class" attribute for a given event and checks that it is the only attribute.\r
- * @param parentEvent\r
- * @return\r
- */\r
- protected String getClassOnlyAttribute(XMLEvent parentEvent) {\r
- return getClassOnlyAttribute(parentEvent, true);\r
- }\r
-\r
-\r
- /**\r
- * Returns the "class" attribute for a given event and checks that it is the only attribute.\r
- * @param parentEvent\r
- * @return\r
- */\r
- protected String getClassOnlyAttribute(XMLEvent parentEvent, boolean required) {\r
- return getOnlyAttribute(parentEvent, CLASS, required);\r
- }\r
- \r
- /**\r
- * Returns the value for the only attribute for a given event and checks that it is the only attribute.\r
- * @param parentEvent\r
- * @return\r
- */\r
- protected String getOnlyAttribute(XMLEvent parentEvent, String attrName, boolean required) {\r
- Map<String, Attribute> attributes = getAttributes(parentEvent);\r
- String classValue =getAndRemoveAttributeValue(parentEvent, attributes, attrName, required, 1);\r
- checkNoAttributes(attributes, parentEvent);\r
- return classValue;\r
- }\r
- \r
- \r
- protected void fireWarningEvent(String message, String locationStr, Integer severity, Integer depth) {\r
- docImport.fireWarningEvent(message, locationStr, severity, depth);\r
- }\r
- \r
- protected void fireWarningEvent(String message, XMLEvent event, Integer severity) {\r
- docImport.fireWarningEvent(message, makeLocationStr(event.getLocation()), severity, 1);\r
- }\r
- \r
- protected void fireSchemaConflictEventExpectedStartTag(String elName, XMLEventReader reader) throws XMLStreamException {\r
- docImport.fireSchemaConflictEventExpectedStartTag(elName, reader);\r
- }\r
-\r
- \r
- protected void fireWarningEvent(String message, String locationStr, int severity) {\r
- docImport.fireWarningEvent(message, locationStr, severity, 1); \r
- }\r
- \r
- protected void fire(IIoEvent event) {\r
- docImport.fire(event);\r
- }\r
- \r
- protected boolean isNotBlank(String str){\r
- return StringUtils.isNotBlank(str);\r
- }\r
- \r
- protected boolean isBlank(String str){\r
- return StringUtils.isBlank(str);\r
- }\r
-\r
- protected TaxonDescription getTaxonDescription(Taxon taxon, Reference<?> ref, boolean isImageGallery, boolean createNewIfNotExists) {\r
- return docImport.getTaxonDescription(taxon, isImageGallery, createNewIfNotExists); \r
- } \r
- \r
-\r
- /**\r
- * Returns the default language defined in the state. If no default language is defined in the state,\r
- * the CDM default language is returned.\r
- * @param state\r
- * @return\r
- */\r
- protected Language getDefaultLanguage(MarkupImportState state) {\r
- Language result = state.getDefaultLanguage();\r
- if (result == null){\r
- result = Language.DEFAULT();\r
- }\r
- return result;\r
- }\r
-\r
-\r
-//*********************** FROM XML IMPORT BASE ****************************************\r
- protected boolean isEndingElement(XMLEvent event, String elName) throws XMLStreamException {\r
- return docImport.isEndingElement(event, elName);\r
- }\r
- \r
- protected boolean isStartingElement(XMLEvent event, String elName) throws XMLStreamException {\r
- return docImport.isStartingElement(event, elName);\r
- }\r
- \r
-\r
- protected void fillMissingEpithetsForTaxa(Taxon parentTaxon, Taxon childTaxon) {\r
- docImport.fillMissingEpithetsForTaxa(parentTaxon, childTaxon); \r
- }\r
- \r
- protected Feature getFeature(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<Feature> voc){\r
- return docImport.getFeature(state, uuid, label, text, labelAbbrev, voc);\r
- }\r
- \r
- protected ExtensionType getExtensionType(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev){\r
- return docImport.getExtensionType(state, uuid, label, text, labelAbbrev);\r
- }\r
- \r
- protected AnnotationType getAnnotationType(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<AnnotationType> voc){\r
- return docImport.getAnnotationType(state, uuid, label, text, labelAbbrev, voc);\r
- }\r
- \r
- protected NamedAreaLevel getNamedAreaLevel(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<NamedAreaLevel> voc){\r
- return docImport.getNamedAreaLevel(state, uuid, label, text, labelAbbrev, voc);\r
- }\r
- \r
- protected NamedArea getNamedArea(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level, TermVocabulary voc, TermMatchMode matchMode){\r
- return docImport.getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level, voc, matchMode);\r
- }\r
- \r
- protected Language getLanguage(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<?> voc){\r
- return docImport.getLanguage(state, uuid, label, text, labelAbbrev, voc);\r
- }\r
- \r
-// *************************************** Concrete methods **********************************************/\r
-\r
-\r
- /**\r
- * @param state\r
- * @param classValue\r
- * @param byAbbrev\r
- * @return\r
- */\r
- protected Rank makeRank(MarkupImportState state, String value, boolean byAbbrev) {\r
- Rank rank = null;\r
- if (StringUtils.isBlank(value)) {\r
- return null;\r
- }\r
- try {\r
- boolean useUnknown = true;\r
- NomenclaturalCode nc = makeNomenclaturalCode(state);\r
- if (value.equals(GENUS_ABBREVIATION)){\r
- rank = Rank.GENUS();\r
- }else if (byAbbrev) {\r
- rank = Rank.getRankByAbbreviation(value, nc, useUnknown);\r
- } else {\r
- rank = Rank.getRankByEnglishName(value, nc, useUnknown);\r
- }\r
- if (rank.equals(Rank.UNKNOWN_RANK())) {\r
- rank = null;\r
- }\r
- } catch (UnknownCdmTypeException e) {\r
- // doNothing\r
- }\r
- return rank;\r
- }\r
-\r
-\r
-\r
- protected TeamOrPersonBase<?> createAuthor(String authorTitle) {\r
- // TODO atomize and also use by name creation\r
- TeamOrPersonBase<?> result = Team.NewTitledInstance(authorTitle, authorTitle);\r
- return result;\r
- }\r
- \r
- protected String getAndRemoveMapKey(Map<String, String> map, String key) {\r
- String result = map.get(key);\r
- map.remove(key);\r
- if (result != null) {\r
- result = normalize(result);\r
- }\r
- return StringUtils.stripToNull(result);\r
- }\r
-\r
-\r
- /**\r
- * Creates a {@link NonViralName} object depending on the defined {@link NomenclaturalCode}\r
- * and the given parameters.\r
- * @param state\r
- * @param rank\r
- * @return\r
- */\r
- protected NonViralName<?> createNameByCode(MarkupImportState state, Rank rank) {\r
- NonViralName<?> name;\r
- NomenclaturalCode nc = makeNomenclaturalCode(state);\r
- name = (NonViralName<?>) nc.getNewTaxonNameInstance(rank);\r
- return name;\r
- }\r
- \r
-\r
- /**\r
- * Returns the {@link NomenclaturalCode} for this import. Default is {@link NomenclaturalCode#ICBN} if\r
- * no code is defined.\r
- * @param state\r
- * @return\r
- */\r
- protected NomenclaturalCode makeNomenclaturalCode(MarkupImportState state) {\r
- NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();\r
- if (nc == null) {\r
- nc = NomenclaturalCode.ICNAFP; // default;\r
- }\r
- return nc;\r
- }\r
-\r
-\r
- /**\r
- * @param state\r
- * @param levelString\r
- * @param next\r
- * @return\r
- */\r
- protected NamedAreaLevel makeNamedAreaLevel(MarkupImportState state, String levelString, XMLEvent next) {\r
- NamedAreaLevel level;\r
- try {\r
- level = state.getTransformer().getNamedAreaLevelByKey(levelString);\r
- if (level == null) {\r
- UUID levelUuid = state.getTransformer().getNamedAreaLevelUuid(levelString);\r
- if (levelUuid == null) {\r
- String message = "Unknown distribution locality class (named area level): %s. Create new level instead.";\r
- message = String.format(message, levelString);\r
- fireWarningEvent(message, next, 6);\r
- }\r
- level = getNamedAreaLevel(state, levelUuid, levelString, levelString, levelString, null);\r
- }\r
- } catch (UndefinedTransformerMethodException e) {\r
- throw new RuntimeException(e);\r
- }\r
- return level;\r
- }\r
- \r
-\r
- /**\r
- * @param state\r
- * @param areaName\r
- * @param level\r
- * @return \r
- */\r
- protected NamedArea makeArea(MarkupImportState state, String areaName, NamedAreaLevel level) {\r
- \r
- //TODO FM vocabulary\r
- TermVocabulary<NamedArea> voc = null; \r
- NamedAreaType areaType = null;\r
- \r
- NamedArea area = null;\r
- try {\r
- area = state.getTransformer().getNamedAreaByKey(areaName);\r
- } catch (UndefinedTransformerMethodException e) {\r
- throw new RuntimeException(e);\r
- }\r
- if (area == null){\r
- boolean isNewInState = false;\r
- UUID uuid = state.getAreaUuid(areaName);\r
- if (uuid == null){\r
- isNewInState = true;\r
- \r
- \r
- try {\r
- uuid = state.getTransformer().getNamedAreaUuid(areaName);\r
- } catch (UndefinedTransformerMethodException e) {\r
- throw new RuntimeException(e);\r
- }\r
- }\r
- \r
- CdmImportBase.TermMatchMode matchMode = CdmImportBase.TermMatchMode.UUID_LABEL;\r
- area = getNamedArea(state, uuid, areaName, areaName, areaName, areaType, level, voc, matchMode);\r
- if (isNewInState){\r
- state.putAreaUuid(areaName, area.getUuid());\r
- \r
- //TODO just for testing -> make generic and move to better place\r
- String geoServiceLayer="vmap0_as_bnd_political_boundary_a";\r
- String layerFieldName ="nam";\r
- \r
- if ("Bangka".equals(areaName)){\r
- String areaValue = "PULAU BANGKA#SUMATERA SELATAN";\r
- GeoServiceArea geoServiceArea = new GeoServiceArea();\r
- geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);\r
- this.editGeoService.setMapping(area, geoServiceArea);\r
-// save(area, state);\r
- }\r
- if ("Luzon".equals(areaName)){\r
- GeoServiceArea geoServiceArea = new GeoServiceArea();\r
- \r
- List<String> list = Arrays.asList("HERMANA MAYOR ISLAND#CENTRAL LUZON",\r
- "HERMANA MENOR ISLAND#CENTRAL LUZON",\r
- "CENTRAL LUZON");\r
- for (String areaValue : list){\r
- geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);\r
- }\r
- \r
- this.editGeoService.setMapping(area, geoServiceArea);\r
-// save(area, state);\r
- }\r
- if ("Mindanao".equals(areaName)){\r
- GeoServiceArea geoServiceArea = new GeoServiceArea();\r
- \r
- List<String> list = Arrays.asList("NORTHERN MINDANAO",\r
- "SOUTHERN MINDANAO",\r
- "WESTERN MINDANAO");\r
- //TODO to be continued\r
- for (String areaValue : list){\r
- geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);\r
- }\r
- \r
- this.editGeoService.setMapping(area, geoServiceArea);\r
-// save(area, state);\r
- }\r
- if ("Palawan".equals(areaName)){\r
- GeoServiceArea geoServiceArea = new GeoServiceArea();\r
- \r
- List<String> list = Arrays.asList("PALAWAN#SOUTHERN TAGALOG");\r
- for (String areaValue : list){\r
- geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);\r
- }\r
- \r
- this.editGeoService.setMapping(area, geoServiceArea);\r
-// save(area, state);\r
- }\r
-\r
- }\r
- }\r
- return area;\r
- }\r
-\r
- \r
- \r
- /**\r
- * Reads character data. Any element other than character data or the ending\r
- * tag will fire an unexpected element event.\r
- *\r
- * @see #getCData(MarkupImportState, XMLEventReader, XMLEvent, boolean)\r
- * @param state\r
- * @param reader\r
- * @param next\r
- * @return\r
- * @throws XMLStreamException\r
- */\r
- protected String getCData(MarkupImportState state, XMLEventReader reader, XMLEvent next) throws XMLStreamException {\r
- return getCData(state, reader, next, true);\r
- }\r
- \r
- /**\r
- * Reads character data. Any element other than character data or the ending\r
- * tag will fire an unexpected element event.\r
- * \r
- * @param state\r
- * @param reader\r
- * @param next\r
- * @param inlineMarkup map for inline markup, this is used for e.g. the locality markup within a subheading\r
- * The map will be filled by the markup element name as key. The value may be a String, a CdmBase or any other object.\r
- * If null any markup text will be neglected but a warning will be fired if they exist.\r
- * @param removeInlineMarkupText if true the markedup text will be removed from the returned String \r
- * @param checkAttributes\r
- * @return\r
- * @throws XMLStreamException\r
- */\r
- protected String getCData(MarkupImportState state, XMLEventReader reader, XMLEvent parent, /*Map<String, Object> inlineMarkup, *boolean removeInlineMarkupText,*/ boolean checkAttributes) throws XMLStreamException {\r
- if (checkAttributes){\r
- checkNoAttributes(parent);\r
- }\r
-\r
- String text = "";\r
- while (reader.hasNext()) {\r
- XMLEvent next = readNoWhitespace(reader);\r
- if (isMyEndingElement(next, parent)) {\r
- return text;\r
- } else if (next.isCharacters()) {\r
- text += next.asCharacters().getData();\r
- } else if (isStartingElement(next, FOOTNOTE_REF)){\r
- handleNotYetImplementedElement(next);\r
-// } else if (isStartingElement(next, LOCALITY)){\r
-// handleCDataLocality(state, reader, parent);\r
- } else {\r
- handleUnexpectedElement(next);\r
- }\r
- }\r
- throw new IllegalStateException("Event has no closing tag");\r
-\r
- }\r
- \r
-// private void handleCDataLocality(MarkupImportState state, XMLEventReader reader, XMLEvent parent) {\r
-// checkAndRemoveAttributeValue(attributes, attrName, value)\r
-// \r
-// }\r
-\r
-\r
-\r
- /**\r
- * For it returns a pure CData annotation string. This behaviour may change in future. More complex annotations\r
- * should be handled differently.\r
- * @param state\r
- * @param reader\r
- * @param parentEvent\r
- * @return\r
- * @throws XMLStreamException\r
- */\r
- protected String handleSimpleAnnotation(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {\r
- String annotation = getCData(state, reader, parentEvent);\r
- return annotation;\r
- }\r
- \r
- /**\r
- * True if text is single "." oder "," or ";" or ":"\r
- * @param text\r
- * @return\r
- */\r
- protected boolean isPunctuation(String text) {\r
- return text == null ? false : text.trim().matches("^[\\.,;:]$");\r
- }\r
-\r
- \r
- protected String getXmlTag(XMLEvent event) {\r
- String result;\r
- if (event.isStartElement()) {\r
- result = "<" + event.asStartElement().getName().getLocalPart()\r
- + ">";\r
- } else if (event.isEndElement()) {\r
- result = "</" + event.asEndElement().getName().getLocalPart() + ">";\r
- } else {\r
- String message = "Only start or end elements are allowed as Html tags";\r
- throw new IllegalStateException(message);\r
- }\r
- return result;\r
- }\r
-\r
- protected WriterDataHolder handleWriter(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {\r
- String text = "";\r
- checkNoAttributes(parentEvent);\r
- WriterDataHolder dataHolder = new WriterDataHolder();\r
- List<FootnoteDataHolder> footnotes = new ArrayList<FootnoteDataHolder>();\r
-\r
- // TODO handle attributes\r
- while (reader.hasNext()) {\r
- XMLEvent next = readNoWhitespace(reader);\r
- if (isMyEndingElement(next, parentEvent)) {\r
- text = CdmUtils.removeBrackets(text);\r
- if (checkMandatoryText(text, parentEvent)) {\r
- text = normalize(text);\r
- dataHolder.writer = text;\r
- dataHolder.footnotes = footnotes;\r
-\r
- // Extension\r
- UUID uuidWriterExtension = MarkupTransformer.uuidWriterExtension;\r
- ExtensionType writerExtensionType = this\r
- .getExtensionType(state, uuidWriterExtension,\r
- "Writer", "writer", "writer");\r
- Extension extension = Extension.NewInstance();\r
- extension.setType(writerExtensionType);\r
- extension.setValue(text);\r
- dataHolder.extension = extension;\r
-\r
- // Annotation\r
- UUID uuidWriterAnnotation = MarkupTransformer.uuidWriterAnnotation;\r
- AnnotationType writerAnnotationType = this.getAnnotationType(state, uuidWriterAnnotation, "Writer", "writer", "writer", null);\r
- Annotation annotation = Annotation.NewInstance(text, writerAnnotationType, getDefaultLanguage(state));\r
- dataHolder.annotation = annotation;\r
-\r
- return dataHolder;\r
- } else {\r
- return null;\r
- }\r
- } else if (isStartingElement(next, FOOTNOTE_REF)) {\r
- FootnoteDataHolder footNote = handleFootnoteRef(state, reader, next);\r
- if (footNote.isRef()) {\r
- footnotes.add(footNote);\r
- } else {\r
- logger.warn("Non ref footnotes not yet impelemnted");\r
- }\r
- } else if (next.isCharacters()) {\r
- text += next.asCharacters().getData();\r
-\r
- } else {\r
- handleUnexpectedElement(next);\r
- state.setUnsuccessfull();\r
- }\r
- }\r
- throw new IllegalStateException("<writer> has no end tag");\r
- }\r
- \r
-\r
- protected void registerFootnotes(MarkupImportState state, AnnotatableEntity entity, List<FootnoteDataHolder> footnotes) {\r
- for (FootnoteDataHolder footNote : footnotes) {\r
- registerFootnoteDemand(state, entity, footNote);\r
- }\r
- }\r
- \r
-\r
- private void registerFootnoteDemand(MarkupImportState state, AnnotatableEntity entity, FootnoteDataHolder footnote) {\r
- FootnoteDataHolder existingFootnote = state.getFootnote(footnote.ref);\r
- if (existingFootnote != null) {\r
- attachFootnote(state, entity, existingFootnote);\r
- } else {\r
- Set<AnnotatableEntity> demands = state.getFootnoteDemands(footnote.ref);\r
- if (demands == null) {\r
- demands = new HashSet<AnnotatableEntity>();\r
- state.putFootnoteDemands(footnote.ref, demands);\r
- }\r
- demands.add(entity);\r
- }\r
- }\r
- \r
-\r
- protected void attachFootnote(MarkupImportState state, AnnotatableEntity entity, FootnoteDataHolder footnote) {\r
- AnnotationType annotationType = this.getAnnotationType(state, MarkupTransformer.uuidFootnote, "Footnote", "An e-flora footnote", "fn", null);\r
- Annotation annotation = Annotation.NewInstance(footnote.string, annotationType, getDefaultLanguage(state));\r
- // TODO transient objects\r
- entity.addAnnotation(annotation);\r
- save(entity, state);\r
- }\r
- \r
-\r
- protected void attachFigure(MarkupImportState state, XMLEvent next, AnnotatableEntity entity, Media figure) {\r
- // IdentifiableEntity<?> toSave;\r
- if (entity.isInstanceOf(TextData.class)) {\r
- TextData deb = CdmBase.deproxy(entity, TextData.class);\r
- deb.addMedia(figure);\r
- // toSave = ((TaxonDescription)deb.getInDescription()).getTaxon();\r
- } else if (entity.isInstanceOf(SpecimenOrObservationBase.class)) {\r
- String message = "figures for specimen should be handled as Textdata";\r
- fireWarningEvent(message, next, 4);\r
- // toSave = ime;\r
- } else if (entity.isInstanceOf(IdentifiableMediaEntity.class)) {\r
- IdentifiableMediaEntity<?> ime = CdmBase.deproxy(entity, IdentifiableMediaEntity.class);\r
- ime.addMedia(figure);\r
- // toSave = ime;\r
- } else {\r
- String message = "Unsupported entity to attach media: %s";\r
- message = String.format(message, entity.getClass().getName());\r
- // toSave = null;\r
- }\r
- save(entity, state);\r
- }\r
- \r
-\r
- protected void registerGivenFootnote(MarkupImportState state, FootnoteDataHolder footnote) {\r
- state.registerFootnote(footnote);\r
- Set<AnnotatableEntity> demands = state.getFootnoteDemands(footnote.id);\r
- if (demands != null) {\r
- for (AnnotatableEntity entity : demands) {\r
- attachFootnote(state, entity, footnote);\r
- }\r
- }\r
- }\r
- \r
-\r
- protected FootnoteDataHolder handleFootnote(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, \r
- MarkupSpecimenImport specimenImport, MarkupNomenclatureImport nomenclatureImport) throws XMLStreamException {\r
- FootnoteDataHolder result = new FootnoteDataHolder();\r
- Map<String, Attribute> attributes = getAttributes(parentEvent);\r
- result.id = getAndRemoveAttributeValue(attributes, ID);\r
- // result.ref = getAndRemoveAttributeValue(attributes, REF);\r
- checkNoAttributes(attributes, parentEvent);\r
-\r
- while (reader.hasNext()) {\r
- XMLEvent next = readNoWhitespace(reader);\r
- if (isStartingElement(next, FOOTNOTE_STRING)) {\r
- String string = handleFootnoteString(state, reader, next, specimenImport, nomenclatureImport);\r
- result.string = string;\r
- } else if (isMyEndingElement(next, parentEvent)) {\r
- return result;\r
- } else {\r
- fireUnexpectedEvent(next, 0);\r
- }\r
- }\r
- return result;\r
- }\r
- \r
-\r
- protected Media handleFigure(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, \r
- MarkupSpecimenImport specimenImport, MarkupNomenclatureImport nomenclatureImport) throws XMLStreamException {\r
- // FigureDataHolder result = new FigureDataHolder();\r
-\r
- Map<String, Attribute> attributes = getAttributes(parentEvent);\r
- String id = getAndRemoveAttributeValue(attributes, ID);\r
- String type = getAndRemoveAttributeValue(attributes, TYPE);\r
- String urlAttr = getAndRemoveAttributeValue(attributes, URL);\r
- checkNoAttributes(attributes, parentEvent);\r
-\r
- String urlString = null;\r
- String legendString = null;\r
- String titleString = null;\r
- String numString = null;\r
- String text = null;\r
- if (isNotBlank(urlAttr)){\r
- urlString = CdmUtils.Nz(state.getBaseMediaUrl()) + urlAttr;\r
- }\r
- while (reader.hasNext()) {\r
- XMLEvent next = readNoWhitespace(reader);\r
- if (isMyEndingElement(next, parentEvent)) {\r
- if (isNotBlank(text)){\r
- fireWarningEvent("Text not yet handled for figures: " + text, next, 4);\r
- }\r
- Media media = makeFigure(state, id, type, urlString, legendString, titleString, numString, next);\r
- return media;\r
- } else if (isStartingElement(next, FIGURE_LEGEND)) {\r
- // TODO same as figure string ?\r
- legendString = handleFootnoteString(state, reader, next, specimenImport, nomenclatureImport);\r
- } else if (isStartingElement(next, FIGURE_TITLE)) {\r
- titleString = getCData(state, reader, next);\r
- } else if (isStartingElement(next, URL)) {\r
- String localUrl = getCData(state, reader, next);\r
- String url = CdmUtils.Nz(state.getBaseMediaUrl()) + localUrl;\r
- if (isBlank(urlString)){\r
- urlString = url;\r
- }\r
- if (! url.equals(urlString)){\r
- String message = "URL attribute and URL element differ. Attribute: %s, Element: %s";\r
- fireWarningEvent(String.format(message, urlString, url), next, 2);\r
- }\r
- } else if (isStartingElement(next, NUM)) {\r
- numString = getCData(state, reader, next);\r
- } else if (next.isCharacters()) {\r
- text += CdmUtils.concat("", text, next.asCharacters().getData());\r
- } else {\r
- fireUnexpectedEvent(next, 0);\r
- }\r
- }\r
- throw new IllegalStateException("<figure> has no end tag");\r
- }\r
-\r
-\r
- /**\r
- * @param state\r
- * @param id\r
- * @param type\r
- * @param urlString\r
- * @param legendString\r
- * @param titleString\r
- * @param numString\r
- * @param next\r
- */\r
- private Media makeFigure(MarkupImportState state, String id, String type, String urlString, \r
- String legendString, String titleString, String numString, XMLEvent next) {\r
- Media media = null;\r
- boolean isFigure = false;\r
- try {\r
- //TODO maybe everything is a figure as it is all taken from a book\r
- if ("lineart".equals(type)) {\r
- isFigure = true;\r
-// media = Figure.NewInstance(url.toURI(), null, null, null);\r
- } else if (type == null || "photo".equals(type)\r
- || "signature".equals(type)\r
- || "others".equals(type)) {\r
- //TODO\r
- } else {\r
- String message = "Unknown figure type '%s'";\r
- message = String.format(message, type);\r
- fireWarningEvent(message, next, 2);\r
- }\r
- media = docImport.getImageMedia(urlString, docImport.getReadMediaData(), isFigure);\r
- \r
- if (media != null){\r
- // title\r
- if (StringUtils.isNotBlank(titleString)) {\r
- media.putTitle(getDefaultLanguage(state), titleString);\r
- }\r
- // legend\r
- if (StringUtils.isNotBlank(legendString)) {\r
- media.putDescription(getDefaultLanguage(state), legendString);\r
- }\r
- if (StringUtils.isNotBlank(numString)) {\r
- // TODO use concrete source (e.g. DAPHNIPHYLLACEAE in FM\r
- // vol.13)\r
- Reference<?> citation = state.getConfig().getSourceReference();\r
- media.addSource(OriginalSourceType.Import, numString, "num", citation, null);\r
- // TODO name used in source if available\r
- }\r
- // TODO which citation\r
- if (StringUtils.isNotBlank(id)) {\r
- media.addSource(OriginalSourceType.Import, id, null, state.getConfig().getSourceReference(), null);\r
- } else {\r
- String message = "Figure id should never be empty or null";\r
- fireWarningEvent(message, next, 6);\r
- }\r
-\r
- // text\r
- // do nothing\r
- registerGivenFigure(state, next, id, media);\r
- \r
- }else{\r
- String message = "No media found: ";\r
- fireWarningEvent(message, next, 4);\r
- }\r
- } catch (MalformedURLException e) {\r
- String message = "Media uri has incorrect syntax: %s";\r
- message = String.format(message, urlString);\r
- fireWarningEvent(message, next, 4);\r
-// } catch (URISyntaxException e) {\r
-// String message = "Media uri has incorrect syntax: %s";\r
-// message = String.format(message, urlString);\r
-// fireWarningEvent(message, next, 4);\r
- }\r
-\r
- return media;\r
- }\r
- \r
-\r
- private void registerGivenFigure(MarkupImportState state, XMLEvent next, String id, Media figure) {\r
- state.registerFigure(id, figure);\r
- Set<AnnotatableEntity> demands = state.getFigureDemands(id);\r
- if (demands != null) {\r
- for (AnnotatableEntity entity : demands) {\r
- attachFigure(state, next, entity, figure);\r
- }\r
- }\r
- save(figure, state);\r
- }\r
- \r
-\r
- private FootnoteDataHolder handleFootnoteRef(MarkupImportState state,\r
- XMLEventReader reader, XMLEvent parentEvent)\r
- throws XMLStreamException {\r
- FootnoteDataHolder result = new FootnoteDataHolder();\r
- Map<String, Attribute> attributes = getAttributes(parentEvent);\r
- result.ref = getAndRemoveAttributeValue(attributes, REF);\r
- checkNoAttributes(attributes, parentEvent);\r
-\r
- // text is not handled, needed only for debugging purposes\r
- String text = "";\r
- while (reader.hasNext()) {\r
- XMLEvent next = readNoWhitespace(reader);\r
- // if (isStartingElement(next, FOOTNOTE_STRING)){\r
- // String string = handleFootnoteString(state, reader, next);\r
- // result.string = string;\r
- // }else\r
- if (isMyEndingElement(next, parentEvent)) {\r
- return result;\r
- } else if (next.isCharacters()) {\r
- text += next.asCharacters().getData();\r
-\r
- } else {\r
- fireUnexpectedEvent(next, 0);\r
- }\r
- }\r
- return result;\r
- }\r
-\r
-\r
-\r
- private String handleFootnoteString(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, MarkupSpecimenImport specimenImport, MarkupNomenclatureImport nomenclatureImport) throws XMLStreamException {\r
- boolean isTextMode = true;\r
- String text = "";\r
- while (reader.hasNext()) {\r
- XMLEvent next = readNoWhitespace(reader);\r
- if (isMyEndingElement(next, parentEvent)) {\r
- return text;\r
- } else if (next.isEndElement()) {\r
- if (isEndingElement(next, FULL_NAME)) {\r
- popUnimplemented(next.asEndElement());\r
- } else if (isEndingElement(next, BR)) {\r
- isTextMode = true;\r
- } else if (isHtml(next)) {\r
- text += getXmlTag(next);\r
- } else {\r
- handleUnexpectedEndElement(next.asEndElement());\r
- }\r
- } else if (next.isStartElement()) {\r
- if (isStartingElement(next, FULL_NAME)) {\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, GATHERING)) {\r
- text += specimenImport.handleInLineGathering(state, reader, next);\r
- } else if (isStartingElement(next, REFERENCES)) {\r
- text += " " + handleInLineReferences(state, reader, next, nomenclatureImport)+ " ";\r
- } else if (isStartingElement(next, BR)) {\r
- text += "<br/>";\r
- isTextMode = false;\r
- } else if (isStartingElement(next, NOMENCLATURE)) {\r
- handleNotYetImplementedElement(next);\r
- } else if (isHtml(next)) {\r
- text += getXmlTag(next);\r
- } else {\r
- handleUnexpectedStartElement(next.asStartElement());\r
- }\r
- } else if (next.isCharacters()) {\r
- if (!isTextMode) {\r
- String message = "footnoteString is not in text mode";\r
- fireWarningEvent(message, next, 6);\r
- } else {\r
- text += next.asCharacters().getData().trim(); \r
- // getCData(state, reader, next); does not work as we have inner tags like <references>\r
- }\r
- } else {\r
- handleUnexpectedEndElement(next.asEndElement());\r
- }\r
- }\r
- throw new IllegalStateException("<footnoteString> has no closing tag");\r
-\r
- }\r
-\r
- private static final List<String> htmlList = Arrays.asList("sub", "sup",\r
- "ol", "ul", "li", "i", "b", "table", "br","tr","td");\r
-\r
- protected boolean isHtml(XMLEvent event) {\r
- if (event.isStartElement()) {\r
- String tag = event.asStartElement().getName().getLocalPart();\r
- return htmlList.contains(tag);\r
- } else if (event.isEndElement()) {\r
- String tag = event.asEndElement().getName().getLocalPart();\r
- return htmlList.contains(tag);\r
- } else {\r
- return false;\r
- }\r
-\r
- }\r
- \r
-\r
- private String handleInLineReferences(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent, MarkupNomenclatureImport nomenclatureImport) throws XMLStreamException {\r
- checkNoAttributes(parentEvent);\r
-\r
- boolean hasReference = false;\r
- String text = "";\r
- while (reader.hasNext()) {\r
- XMLEvent next = readNoWhitespace(reader);\r
- if (isMyEndingElement(next, parentEvent)) {\r
- checkMandatoryElement(hasReference, parentEvent.asStartElement(), REFERENCE);\r
- return text;\r
- } else if (isStartingElement(next, REFERENCE)) {\r
- text += handleInLineReference(state, reader, next, nomenclatureImport);\r
- hasReference = true;\r
- } else {\r
- handleUnexpectedElement(next);\r
- }\r
- }\r
- throw new IllegalStateException("<References> has no closing tag");\r
- }\r
-\r
- private String handleInLineReference(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent, MarkupNomenclatureImport nomenclatureImport)throws XMLStreamException {\r
- Reference<?> reference = nomenclatureImport.handleReference(state, reader, parentEvent);\r
- String result = "<cdm:ref uuid='%s'>%s</ref>";\r
- result = String.format(result, reference.getUuid(), reference.getTitleCache());\r
- save(reference, state);\r
- return result;\r
- }\r
- \r
-\r
- /**\r
- * Handle string\r
- * @param state\r
- * @param reader\r
- * @param parentEvent\r
- * @param feature only needed for distributionLocalities\r
- * @return\r
- * @throws XMLStreamException\r
- */\r
- protected Map<String, String> handleString(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, Feature feature)throws XMLStreamException {\r
- // attributes\r
- String classValue = getClassOnlyAttribute(parentEvent, false);\r
- if (StringUtils.isNotBlank(classValue)) {\r
- String message = "class attribute for <string> not yet implemented";\r
- fireWarningEvent(message, parentEvent, 2);\r
- }\r
-\r
- // subheadings\r
- Map<String, String> subHeadingMap = new HashMap<String, String>();\r
- String currentSubheading = null;\r
-\r
- boolean isTextMode = true;\r
- String text = "";\r
- while (reader.hasNext()) {\r
- XMLEvent next = readNoWhitespace(reader);\r
- if (isMyEndingElement(next, parentEvent)) {\r
- putCurrentSubheading(subHeadingMap, currentSubheading, text);\r
- return subHeadingMap;\r
- } else if (isStartingElement(next, BR)) {\r
- text += "<br/>";\r
- isTextMode = false;\r
- } else if (isEndingElement(next, BR)) {\r
- isTextMode = true;\r
- } else if (isHtml(next)) {\r
- text += getXmlTag(next);\r
- } else if (isStartingElement(next, SUB_HEADING)) {\r
- text = putCurrentSubheading(subHeadingMap,currentSubheading, text);\r
- // TODO footnotes\r
- currentSubheading = getCData(state, reader, next).trim();\r
- } else if (isStartingElement(next, DISTRIBUTION_LOCALITY)) {\r
- if (feature != null && !feature.equals(Feature.DISTRIBUTION())) {\r
- String message = "Distribution locality only allowed for feature of type 'distribution'";\r
- fireWarningEvent(message, next, 4);\r
- }\r
- text += handleDistributionLocality(state, reader, next);\r
- } else if (next.isCharacters()) {\r
- if (! isTextMode) {\r
- String message = "String is not in text mode";\r
- fireWarningEvent(message, next, 6);\r
- } else {\r
- text += next.asCharacters().getData();\r
- }\r
- } else if (isStartingElement(next, HEADING)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, VERNACULAR_NAMES)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, QUOTE)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, DEDICATION)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, TAXONTYPE)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, FULL_NAME)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- }else if (isStartingElement(next, REFERENCES)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, GATHERING)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, ANNOTATION)) {\r
- //TODO //TODO test handleSimpleAnnotation\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, HABITAT)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, FIGURE_REF)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, FIGURE)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, FOOTNOTE_REF)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, FOOTNOTE)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, WRITER)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isStartingElement(next, DATES)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else {\r
- handleUnexpectedElement(next);\r
- }\r
- }\r
- throw new IllegalStateException("<String> has no closing tag");\r
- }\r
- \r
-\r
- /**\r
- * @param subHeadingMap\r
- * @param currentSubheading\r
- * @param text\r
- * @return\r
- */\r
- private String putCurrentSubheading(Map<String, String> subHeadingMap, String currentSubheading, String text) {\r
- if (StringUtils.isNotBlank(text)) {\r
- text = removeStartingMinus(text);\r
- subHeadingMap.put(currentSubheading, text.trim());\r
- }\r
- return "";\r
- }\r
-\r
- private String removeStartingMinus(String string) {\r
- string = replaceStart(string, "-");\r
- string = replaceStart(string, "\u002d");\r
- string = replaceStart(string, "\u2013");\r
- string = replaceStart(string, "\u2014");\r
- string = replaceStart(string, "--");\r
- return string;\r
- }\r
- \r
- \r
- /**\r
- * @param value\r
- * @param replacementString\r
- */\r
- private String replaceStart(String value, String replacementString) {\r
- if (value.startsWith(replacementString) ){\r
- value = value.substring(replacementString.length()).trim();\r
- }\r
- while (value.startsWith("-") || value.startsWith("\u2014") ){\r
- value = value.substring("-".length()).trim();\r
- }\r
- return value;\r
- }\r
- \r
-\r
- private String handleDistributionLocality(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent)throws XMLStreamException {\r
- Map<String, Attribute> attributes = getAttributes(parentEvent);\r
- String classValue = getAndRemoveRequiredAttributeValue(parentEvent, attributes, CLASS);\r
- String statusValue =getAndRemoveAttributeValue(attributes, STATUS);\r
- String frequencyValue =getAndRemoveAttributeValue(attributes, FREQUENCY);\r
- \r
-\r
- Taxon taxon = state.getCurrentTaxon();\r
- // TODO which ref to take?\r
- Reference<?> ref = state.getConfig().getSourceReference();\r
-\r
- String text = "";\r
- while (reader.hasNext()) {\r
- XMLEvent next = readNoWhitespace(reader);\r
- if (isMyEndingElement(next, parentEvent)) {\r
- if (StringUtils.isNotBlank(text)) {\r
- String label = CdmUtils.removeTrailingDot(normalize(text));\r
- TaxonDescription description = getTaxonDescription(taxon, ref, false, true);\r
- NamedAreaLevel level = makeNamedAreaLevel(state,classValue, next);\r
- \r
- //status\r
- PresenceAbsenceTermBase<?> status = null;\r
- if (isNotBlank(statusValue)){\r
- try {\r
- status = state.getTransformer().getPresenceTermByKey(statusValue);\r
- if (status == null){\r
- //TODO\r
- String message = "The presence/absence status '%s' could not be transformed to an CDM status"; \r
- fireWarningEvent(String.format(message, statusValue), next, 4);\r
- }\r
- } catch (UndefinedTransformerMethodException e) {\r
- throw new RuntimeException(e);\r
- }\r
- }else{\r
- status = PresenceTerm.PRESENT();\r
- }\r
- //frequency\r
- if (isNotBlank(frequencyValue)){\r
- String message = "The frequency attribute is currently not yet available in CDM";\r
- fireWarningEvent(message, parentEvent, 6);\r
- }\r
- \r
- NamedArea higherArea = null;\r
- List<NamedArea> areas = new ArrayList<NamedArea>(); \r
- \r
- String patSingleArea = "([^,\\(]{3,})";\r
- String patSeparator = "(,|\\sand\\s)";\r
- String hierarchiePattern = String.format("%s\\((%s(%s%s)*)\\)",patSingleArea, patSingleArea, patSeparator, patSingleArea);\r
- Pattern patHierarchie = Pattern.compile(hierarchiePattern, Pattern.CASE_INSENSITIVE);\r
- Matcher matcher = patHierarchie.matcher(label); \r
- if (matcher.matches()){\r
- String higherAreaStr = matcher.group(1).trim();\r
- higherArea = makeArea(state, higherAreaStr, level);\r
- String[] innerAreas = matcher.group(2).split(patSeparator);\r
- for (String innerArea : innerAreas){\r
- if (isNotBlank(innerArea)){\r
- NamedArea singleArea = makeArea(state, innerArea.trim(), level);\r
- areas.add(singleArea);\r
- NamedArea partOf = singleArea.getPartOf();\r
-// if (partOf == null){\r
-// singleArea.setPartOf(higherArea);\r
-// }\r
- }\r
- }\r
- }else{\r
- NamedArea singleArea = makeArea(state, label, level);\r
- areas.add(singleArea);\r
- }\r
- \r
- for (NamedArea area : areas){\r
- //create distribution\r
- Distribution distribution = Distribution.NewInstance(area,status);\r
- description.addElement(distribution);\r
- }\r
- } else {\r
- String message = "Empty distribution locality";\r
- fireWarningEvent(message, next, 4);\r
- }\r
- return text;\r
- } else if (isStartingElement(next, COORDINATES)) {\r
- //TODO\r
- handleNotYetImplementedElement(next);\r
- } else if (isEndingElement(next, COORDINATES)) {\r
- //TODO\r
- popUnimplemented(next.asEndElement());\r
- } else if (next.isCharacters()) {\r
- text += next.asCharacters().getData();\r
- } else {\r
- handleUnexpectedElement(next);\r
- }\r
- }\r
- throw new IllegalStateException("<DistributionLocality> has no closing tag");\r
- } \r
-\r
- \r
-//********************************************** OLD ************************************* \r
-\r
-// protected boolean testAdditionalElements(Element parentElement, List<String> excludeList){\r
-// boolean result = true;\r
-// List<Element> list = parentElement.getChildren();\r
-// for (Element element : list){\r
-// if (! excludeList.contains(element.getName())){\r
-// logger.warn("Unknown element (" + element.getName() + ") in parent element (" + parentElement.getName() + ")");\r
-// result = false;\r
-// }\r
-// }\r
-// return result;\r
-// }\r
-// \r
-// \r
-// protected <T extends IdentifiableEntity> T makeReferenceType(Element element, Class<? extends T> clazz, MapWrapper<? extends T> objectMap, ResultWrapper<Boolean> success){\r
-// T result = null;\r
-// String linkType = element.getAttributeValue("linkType");\r
-// String ref = element.getAttributeValue("ref");\r
-// if(ref == null && linkType == null){\r
-// result = getInstance(clazz);\r
-// if (result != null){\r
-// String title = element.getTextNormalize();\r
-// result.setTitleCache(title, true);\r
-// }\r
-// }else if (linkType == null || linkType.equals("local")){\r
-// //TODO\r
-// result = objectMap.get(ref);\r
-// if (result == null){\r
-// logger.warn("Object (ref = " + ref + ")could not be found in WrapperMap");\r
-// }\r
-// }else if(linkType.equals("external")){\r
-// logger.warn("External link types not yet implemented");\r
-// }else if(linkType.equals("other")){\r
-// logger.warn("Other link types not yet implemented");\r
-// }else{\r
-// logger.warn("Unknown link type or missing ref");\r
-// }\r
-// if (result == null){\r
-// success.setValue(false);\r
-// }\r
-// return result;\r
-// }\r
-// \r
-// \r
-// protected Reference makeAccordingTo(Element elAccordingTo, MapWrapper<Reference> referenceMap, ResultWrapper<Boolean> success){\r
-// Reference result = null;\r
-// if (elAccordingTo != null){\r
-// String childName = "AccordingToDetailed";\r
-// boolean obligatory = false;\r
-// Element elAccordingToDetailed = XmlHelp.getSingleChildElement(success, elAccordingTo, childName, elAccordingTo.getNamespace(), obligatory);\r
-//\r
-// childName = "Simple";\r
-// obligatory = true;\r
-// Element elSimple = XmlHelp.getSingleChildElement(success, elAccordingTo, childName, elAccordingTo.getNamespace(), obligatory);\r
-// \r
-// if (elAccordingToDetailed != null){\r
-// result = makeAccordingToDetailed(elAccordingToDetailed, referenceMap, success);\r
-// }else{\r
-// result = ReferenceFactory.newGeneric();\r
-// String title = elSimple.getTextNormalize();\r
-// result.setTitleCache(title, true);\r
-// }\r
-// }\r
-// return result;\r
-// }\r
-// \r
-// \r
-// private Reference makeAccordingToDetailed(Element elAccordingToDetailed, MapWrapper<Reference> referenceMap, ResultWrapper<Boolean> success){\r
-// Reference result = null;\r
-// Namespace tcsNamespace = elAccordingToDetailed.getNamespace();\r
-// if (elAccordingToDetailed != null){\r
-// //AuthorTeam\r
-// String childName = "AuthorTeam";\r
-// boolean obligatory = false;\r
-// Element elAuthorTeam = XmlHelp.getSingleChildElement(success, elAccordingToDetailed, childName, tcsNamespace, obligatory);\r
-// makeAccordingToAuthorTeam(elAuthorTeam, success);\r
-// \r
-// //PublishedIn\r
-// childName = "PublishedIn";\r
-// obligatory = false;\r
-// Element elPublishedIn = XmlHelp.getSingleChildElement(success, elAccordingToDetailed, childName, tcsNamespace, obligatory);\r
-// result = makeReferenceType(elPublishedIn, Reference.class, referenceMap, success);\r
-// \r
-// //MicroReference\r
-// childName = "MicroReference";\r
-// obligatory = false;\r
-// Element elMicroReference = XmlHelp.getSingleChildElement(success, elAccordingToDetailed, childName, tcsNamespace, obligatory);\r
-// String microReference = elMicroReference.getTextNormalize();\r
-// if (CdmUtils.Nz(microReference).equals("")){\r
-// //TODO\r
-// logger.warn("MicroReference not yet implemented for AccordingToDetailed"); \r
-// }\r
-// }\r
-// return result;\r
-// }\r
-//\r
-// private Team makeAccordingToAuthorTeam(Element elAuthorTeam, ResultWrapper<Boolean> succes){\r
-// Team result = null;\r
-// if (elAuthorTeam != null){\r
-// //TODO\r
-// logger.warn("AuthorTeam not yet implemented for AccordingToDetailed");\r
-// }\r
-// return result;\r
-// }\r
-\r
-\r
-\r
-}\r
+/**
+* Copyright (C) 2007 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+
+package eu.etaxonomy.cdm.io.markup;
+
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.Characters;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.WordUtils;
+import org.apache.log4j.Logger;
+
+import eu.etaxonomy.cdm.api.service.IClassificationService;
+import eu.etaxonomy.cdm.api.service.ITermService;
+import eu.etaxonomy.cdm.common.CdmUtils;
+import eu.etaxonomy.cdm.ext.geo.GeoServiceArea;
+import eu.etaxonomy.cdm.ext.geo.IEditGeoService;
+import eu.etaxonomy.cdm.io.common.CdmImportBase;
+import eu.etaxonomy.cdm.io.common.CdmImportBase.TermMatchMode;
+import eu.etaxonomy.cdm.io.common.events.IIoEvent;
+import eu.etaxonomy.cdm.io.common.events.IoProblemEvent;
+import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
+import eu.etaxonomy.cdm.model.agent.Team;
+import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
+import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
+import eu.etaxonomy.cdm.model.common.Annotation;
+import eu.etaxonomy.cdm.model.common.AnnotationType;
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.common.DefinedTerm;
+import eu.etaxonomy.cdm.model.common.DefinedTermBase;
+import eu.etaxonomy.cdm.model.common.Extension;
+import eu.etaxonomy.cdm.model.common.ExtensionType;
+import eu.etaxonomy.cdm.model.common.Language;
+import eu.etaxonomy.cdm.model.common.MarkerType;
+import eu.etaxonomy.cdm.model.common.OriginalSourceType;
+import eu.etaxonomy.cdm.model.common.TermVocabulary;
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+import eu.etaxonomy.cdm.model.description.Distribution;
+import eu.etaxonomy.cdm.model.description.Feature;
+import eu.etaxonomy.cdm.model.description.PolytomousKey;
+import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
+import eu.etaxonomy.cdm.model.description.TaxonDescription;
+import eu.etaxonomy.cdm.model.description.TextData;
+import eu.etaxonomy.cdm.model.location.NamedArea;
+import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
+import eu.etaxonomy.cdm.model.location.NamedAreaType;
+import eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity;
+import eu.etaxonomy.cdm.model.media.Media;
+import eu.etaxonomy.cdm.model.name.INonViralName;
+import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
+import eu.etaxonomy.cdm.model.name.NonViralName;
+import eu.etaxonomy.cdm.model.name.Rank;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
+import eu.etaxonomy.cdm.model.reference.Reference;
+import eu.etaxonomy.cdm.model.taxon.Classification;
+import eu.etaxonomy.cdm.model.taxon.Taxon;
+import eu.etaxonomy.cdm.model.taxon.TaxonBase;
+import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
+import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
+
+/**
+ * @author a.mueller
+ * @created 04.08.2008
+ */
+public abstract class MarkupImportBase {
+ private static final Logger logger = Logger.getLogger(MarkupImportBase.class);
+
+ //Base
+ protected static final String ALTITUDE = "altitude";
+ protected static final String ANNOTATION = "annotation";
+ protected static final String BOLD = "bold";
+ protected static final String BR = "br";
+ protected static final String DOUBTFUL = "doubtful";
+ protected static final String CITATION = "citation";
+ protected static final String CLASS = "class";
+ protected static final String COORDINATES = "coordinates";
+ protected static final String DATES = "dates";
+ protected static final String GATHERING = "gathering";
+ protected static final String GATHERING_GROUP = "gatheringGroup";
+ protected static final String GENUS_ABBREVIATION = "genus abbreviation";
+ protected static final String FOOTNOTE = "footnote";
+ protected static final String FOOTNOTE_REF = "footnoteRef";
+ protected static final String FULL_NAME = "fullName";
+ protected static final String ITALICS = "italics";
+ protected static final String NUM = "num";
+ protected static final String NOTES = "notes";
+ protected static final String PUBLICATION = "publication";
+ protected static final String SPECIMEN_TYPE = "specimenType";
+ protected static final String STATUS = "status";
+ protected static final String SUB_HEADING = "subHeading";
+ protected static final String TYPE = "type";
+ protected static final String TYPE_STATUS = "typeStatus";
+ protected static final String UNKNOWN = "unknown";
+
+
+ protected static final boolean CREATE_NEW = true;
+ protected static final boolean NO_IMAGE_GALLERY = false;
+ protected static final boolean IMAGE_GALLERY = true;
+
+ protected static final String ADDENDA = "addenda";
+ protected static final String BIBLIOGRAPHY = "bibliography";
+ protected static final String BIOGRAPHIES = "biographies";
+ protected static final String CHAR = "char";
+ protected static final String DEDICATION = "dedication";
+ protected static final String DEFAULT_MEDIA_URL = "defaultMediaUrl";
+ protected static final String DISTRIBUTION_LIST = "distributionList";
+ protected static final String DISTRIBUTION_LOCALITY = "distributionLocality";
+ protected static final String FEATURE = "feature";
+ protected static final String FIGURE = "figure";
+ protected static final String FIGURE_LEGEND = "figureLegend";
+ protected static final String FIGURE_PART = "figurePart";
+ protected static final String FIGURE_REF = "figureRef";
+ protected static final String FIGURE_TITLE = "figureTitle";
+ protected static final String FOOTNOTE_STRING = "footnoteString";
+ protected static final String FREQUENCY = "frequency";
+ protected static final String HEADING = "heading";
+ protected static final String HABITAT = "habitat";
+ protected static final String HABITAT_LIST = "habitatList";
+ protected static final String IS_FREETEXT = "isFreetext";
+ protected static final String ID = "id";
+ protected static final String KEY = "key";
+ protected static final String LIFE_CYCLE_PERIODS = "lifeCyclePeriods";
+ protected static final String META_DATA = "metaData";
+ protected static final String MODS = "mods";
+
+ protected static final String NOMENCLATURE = "nomenclature";
+ protected static final String QUOTE = "quote";
+ protected static final String RANK = "rank";
+ protected static final String REF = "ref";
+ protected static final String REF_NUM = "refNum";
+ protected static final String REFERENCE = "reference";
+ protected static final String REFERENCES = "references";
+ protected static final String SUB_CHAR = "subChar";
+ protected static final String TAXON = "taxon";
+ protected static final String TAXONTITLE = "taxontitle";
+ protected static final String TAXONTYPE = "taxontype";
+ protected static final String TEXT_SECTION = "textSection";
+ protected static final String TREATMENT = "treatment";
+ protected static final String SERIALS_ABBREVIATIONS = "serialsAbbreviations";
+ protected static final String STRING = "string";
+ protected static final String URL = "url";
+ protected static final String WRITER = "writer";
+
+ protected static final String LOCALITY = "locality";
+
+
+
+ //Nomenclature
+ protected static final String ACCEPTED = "accepted";
+ protected static final String ACCEPTED_NAME = "acceptedName";
+ protected static final String ALTERNATEPUBTITLE = "alternatepubtitle";
+ protected static final String APPENDIX = "appendix";
+ protected static final String AUTHOR = "author";
+ protected static final String DETAILS = "details";
+ protected static final String EDITION = "edition";
+ protected static final String EDITORS = "editors";
+ protected static final String HOMONYM = "homonym";
+ protected static final String HOMOTYPES = "homotypes";
+ protected static final String NOMENCLATURAL_NOTES = "nomenclaturalNotes";
+ protected static final String INFRANK = "infrank";
+ protected static final String INFRAUT = "infraut";
+ protected static final String INFRPARAUT = "infrparaut";
+ protected static final String ISSUE = "issue";
+ protected static final String NAME = "name";
+ protected static final String NAME_TYPE = "nameType";
+ protected static final String NOM = "nom";
+ protected static final String PAGES = "pages";
+ protected static final String PARAUT = "paraut";
+ protected static final String PUBFULLNAME = "pubfullname";
+ protected static final String PUBLOCATION = "publocation";
+ protected static final String PUBLISHER = "publisher";
+ protected static final String PUBNAME = "pubname";
+ protected static final String PUBTITLE = "pubtitle";
+ protected static final String PUBTYPE = "pubtype";
+ protected static final String REF_PART = "refPart";
+ protected static final String SYNONYM = "synonym";
+ protected static final String USAGE = "usage";
+ protected static final String VOLUME = "volume";
+ protected static final String YEAR = "year";
+
+
+ //keys
+ protected static final String COUPLET = "couplet";
+ protected static final String IS_SPOTCHARACTERS = "isSpotcharacters";
+ protected static final String ONLY_NUMBERED_TAXA_EXIST = "onlyNumberedTaxaExist";
+ protected static final String EXISTS = "exists";
+ protected static final String KEYNOTES = "keynotes";
+ protected static final String KEY_TITLE = "keyTitle";
+ protected static final String QUESTION = "question";
+ protected static final String TEXT = "text";
+ protected static final String TO_COUPLET = "toCouplet";
+ protected static final String TO_KEY = "toKey";
+ protected static final String TO_TAXON = "toTaxon";
+
+
+ //Feature
+ protected static final String VERNACULAR_NAMES = "vernacularNames";
+ protected static final String VERNACULAR_NAME = "vernacularName";
+ protected static final String TRANSLATION = "translation";
+ protected static final String LOCAL_LANGUAGE = "localLanguage";
+
+
+
+ protected MarkupDocumentImport docImport;
+
+ private final IEditGeoService editGeoService;
+ protected MarkupFeatureImport featureImport;
+
+ public MarkupImportBase(MarkupDocumentImport docImport) {
+ super();
+ this.docImport = docImport;
+ this.editGeoService = docImport.getEditGeoService();
+ }
+
+ private final Stack<QName> unhandledElements = new Stack<QName>();
+ private final Stack<QName> handledElements = new Stack<QName>();
+
+
+ protected <T extends CdmBase> void save(Collection<T> collection, MarkupImportState state) {
+ if (state.isCheck() || collection.isEmpty()){
+ return;
+ }
+ T example = collection.iterator().next();
+ if (example.isInstanceOf(TaxonBase.class)){
+ Collection<TaxonBase> typedCollection = (Collection<TaxonBase>)collection;
+ docImport.getTaxonService().saveOrUpdate(typedCollection);
+ }else if (example.isInstanceOf(Classification.class)){
+ Collection<Classification> typedCollection = (Collection<Classification>)collection;
+ docImport.getClassificationService().saveOrUpdate(typedCollection);
+ }else if (example.isInstanceOf(PolytomousKey.class)){
+ Collection<PolytomousKey> typedCollection = (Collection<PolytomousKey>)collection;
+ docImport.getPolytomousKeyService().saveOrUpdate(typedCollection);
+ }else if (example.isInstanceOf(DefinedTermBase.class)){
+ Collection<DefinedTermBase> typedCollection = (Collection<DefinedTermBase>)collection;
+ getTermService().saveOrUpdate(typedCollection);
+ }
+
+ }
+
+
+ //TODO move to service layer for all IdentifiableEntities
+ protected void save(CdmBase cdmBase, MarkupImportState state) {
+ if (state.isCheck()){
+ return;
+ }
+ cdmBase = CdmBase.deproxy(cdmBase, CdmBase.class);
+ if (cdmBase == null){
+ String message = "Tried to save a null object.";
+ fireWarningEvent(message, "--location ?? --", 6,1);
+ } else if (cdmBase.isInstanceOf(TaxonBase.class)){
+ docImport.getTaxonService().saveOrUpdate((TaxonBase<?>)cdmBase);
+ }else if (cdmBase.isInstanceOf(Classification.class)){
+ docImport.getClassificationService().saveOrUpdate((Classification)cdmBase);
+ }else if (cdmBase.isInstanceOf(PolytomousKey.class)){
+ docImport.getPolytomousKeyService().saveOrUpdate((PolytomousKey)cdmBase);
+ }else if (cdmBase.isInstanceOf(DefinedTermBase.class)){
+ docImport.getTermService().saveOrUpdate((DefinedTermBase<?>)cdmBase);
+ }else if (cdmBase.isInstanceOf(Media.class)){
+ docImport.getMediaService().saveOrUpdate((Media)cdmBase);
+ }else if (cdmBase.isInstanceOf(SpecimenOrObservationBase.class)){
+ docImport.getOccurrenceService().saveOrUpdate((SpecimenOrObservationBase<?>)cdmBase);
+ }else if (cdmBase.isInstanceOf(DescriptionElementBase.class)){
+ docImport.getDescriptionService().saveDescriptionElement((DescriptionElementBase)cdmBase);
+ }else if (cdmBase.isInstanceOf(Reference.class)){
+ docImport.getReferenceService().saveOrUpdate((Reference)cdmBase);
+ }else{
+ String message = "Unknown cdmBase type to save: " + cdmBase.getClass();
+ fireWarningEvent(message, "Unknown location", 8);
+ }
+ //logger.warn("Saved " + cdmBase);
+ }
+
+
+ protected ITermService getTermService() {
+ return docImport.getTermService();
+ }
+
+ protected IClassificationService getClassificationService() {
+ return docImport.getClassificationService();
+ }
+
+//*********************** Attribute methods *************************************/
+
+ /**
+ * Returns a map for all attributes of an start element
+ * @param event
+ * @return
+ */
+ protected Map<String, Attribute> getAttributes(XMLEvent event) {
+ Map<String, Attribute> result = new HashMap<String, Attribute>();
+ if (!event.isStartElement()){
+ fireWarningEvent("Event is not an startElement. Can't check attributes", makeLocationStr(event.getLocation()), 1, 1);
+ return result;
+ }
+ StartElement element = event.asStartElement();
+ Iterator<Attribute> attributes = element.getAttributes();
+ while (attributes.hasNext()){
+ Attribute attribute = attributes.next();
+ //TODO namespaces
+ result.put(attribute.getName().getLocalPart(), attribute);
+ }
+ return result;
+ }
+
+ /**
+ * Throws an unexpected attributes event if the event has any attributes.
+ * @param event
+ */
+ protected void checkNoAttributes(Map<String, Attribute> attributes, XMLEvent event) {
+ String[] exceptions = new String[]{};
+ handleUnexpectedAttributes(event.getLocation(), attributes, 1, exceptions);
+ }
+
+
+
+ /**
+ * Throws an unexpected attributes event if the event has any attributes.
+ * @param event
+ */
+ protected void checkNoAttributes(XMLEvent event) {
+ String[] exceptions = new String[]{};
+ checkNoAttributes(event, 1, exceptions);
+ }
+
+ /**
+ * Throws an unexpected attributes event if the event has any attributes except those mentioned in "exceptions".
+ * @param event
+ * @param exceptions
+ */
+ protected void checkNoAttributes(XMLEvent event, int stackDepth, String... exceptions) {
+ if (! event.isStartElement()){
+ fireWarningEvent("Event is not an startElement. Can't check attributes", makeLocationStr(event.getLocation()), 1, 1);
+ return;
+ }
+ StartElement startElement = event.asStartElement();
+ Map<String, Attribute> attributes = getAttributes(startElement);
+ handleUnexpectedAttributes(startElement.getLocation(), attributes, stackDepth+1, exceptions);
+ }
+
+
+ /**
+ * Checks if the given attribute exists and has the given value.
+ * If yes, true is returned and the attribute is removed from the attributes map.
+ * Otherwise false is returned.
+ * @param attributes
+ * @param attrName
+ * @param value
+ * @return <code>true</code> if attribute has given value, <code>false</code> otherwise
+ */
+ protected boolean checkAndRemoveAttributeValue( Map<String, Attribute> attributes, String attrName, String value) {
+ Attribute attr = attributes.get(attrName);
+ if (attr == null ||value == null ){
+ return false;
+ }else{
+ if (value.equals(attr.getValue())){
+ attributes.remove(attrName);
+ return true;
+ }else{
+ return false;
+ }
+ }
+ }
+
+
+ /**
+ * Returns the value of a given attribute name and removes the attribute from the attributes map.
+ * @param attributes
+ * @param attrName
+ * @return
+ */
+ protected String getAndRemoveAttributeValue(Map<String, Attribute> attributes, String attrName) {
+ return getAndRemoveAttributeValue(null, attributes, attrName, false, 1);
+ }
+
+ /**
+ * Returns the value of a boolean attribute with the given name and removes the attribute from the attributes map.
+ * Returns <code>defaultValue</code> if the attribute does not exist. ALso returns <code>defaultValue</code> and throws a warning if the
+ * attribute has no boolean value (true, false).
+ * @param
+ * @param attributes the
+ * @param attrName the name of the attribute
+ * @param defaultValue the default value to return if attribute does not exist or can not be defined
+ * @return
+ */
+ protected Boolean getAndRemoveBooleanAttributeValue(XMLEvent event, Map<String, Attribute> attributes, String attrName, Boolean defaultValue) {
+ String value = getAndRemoveAttributeValue(null, attributes, attrName, false, 1);
+ Boolean result = defaultValue;
+ if (value != null){
+ if (value.equalsIgnoreCase("true")){
+ result = true;
+ }else if (value.equalsIgnoreCase("false")){
+ result = false;
+ }else{
+ String message = "Boolean attribute has no boolean value ('true', 'false') but '%s'";
+ fireWarningEvent(String.format(message, value), makeLocationStr(event.getLocation()), 6, 1);
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * Returns the value of a given attribute name and returns the attribute from the attributes map.
+ * Fires a mandatory field is missing event if the attribute does not exist.
+ * @param xmlEvent
+ * @param attributes
+ * @param attrName
+ * @return
+ */
+ protected String getAndRemoveRequiredAttributeValue(XMLEvent xmlEvent, Map<String, Attribute> attributes, String attrName) {
+ return getAndRemoveAttributeValue(xmlEvent, attributes, attrName, true, 1);
+ }
+
+ /**
+ * Returns the value of a given attribute name and returns the attribute from the attributes map.
+ * If required is <code>true</code> and the attribute does not exist a mandatory field is missing event is fired.
+ * @param xmlEvent
+ * @param attributes
+ * @param attrName
+ * @param isRequired
+ * @return
+ */
+ private String getAndRemoveAttributeValue(XMLEvent xmlEvent, Map<String, Attribute> attributes, String attrName, boolean isRequired, int stackDepth) {
+ Attribute attr = attributes.get(attrName);
+ if (attr == null ){
+ if (isRequired){
+ fireMandatoryElementIsMissing(xmlEvent, attrName, 8, stackDepth+1);
+ }
+ return null;
+ }else{
+ attributes.remove(attrName);
+ return attr.getValue();
+ }
+ }
+
+ /**
+ * Fires an not yet implemented event if the given attribute exists in attributes.
+ * @param attributes
+ * @param attrName
+ */
+ protected void handleNotYetImplementedAttribute(Map<String, Attribute> attributes, String attrName) {
+ Attribute attr = attributes.get(attrName);
+ if (attr != null){
+ attributes.remove(attrName);
+ QName qName = attr.getName();
+ fireNotYetImplementedAttribute(attr.getLocation(), qName, 1);
+ }
+ }
+
+ /**
+ * Fires an unhandled attributes event, if attributes exist in attributes map not covered by the exceptions.
+ * No event is fired if the unhandled elements stack is not empty.
+ * @param location
+ * @param attributes
+ * @param exceptions
+ */
+ protected void handleUnexpectedAttributes(Location location,Map<String, Attribute> attributes, String... exceptions) {
+ handleUnexpectedAttributes(location, attributes, 1, exceptions);
+ }
+
+ /**
+ * see {@link #handleUnexpectedAttributes(Location, Map, String...)}
+ *
+ * @param location
+ * @param attributes
+ * @param stackDepth the stack trace depth
+ * @param exceptions
+ */
+ private void handleUnexpectedAttributes(Location location,Map<String, Attribute> attributes, int stackDepth, String... exceptions) {
+ if (attributes.size() > 0){
+ if (this.unhandledElements.size() == 0 ){
+ boolean hasUnhandledAttributes = false;
+ for (String key : attributes.keySet()){
+ boolean isException = false;
+ for (String exception : exceptions){
+ if(key.equals(exception)){
+ isException = true;
+ }
+ }
+ if (!isException){
+ hasUnhandledAttributes = true;
+ }
+ }
+ if (hasUnhandledAttributes){
+ fireUnexpectedAttributes(location, attributes, stackDepth+1);
+ }
+ }
+ }
+ }
+
+
+ private void fireUnexpectedAttributes(Location location, Map<String, Attribute> attributes, int stackDepth) {
+ String attributesString = "";
+ for (String key : attributes.keySet()){
+ Attribute attribute = attributes.get(key);
+ attributesString = CdmUtils.concat(",", attributesString, attribute.getName().getLocalPart() + ":" + attribute.getValue());
+ }
+ String message = "Unexpected attributes: %s";
+ IoProblemEvent event = makeProblemEvent(location, String.format(message, attributesString), 1 , stackDepth +1 );
+ fire(event);
+ }
+
+
+ protected void fireUnexpectedAttributeValue(XMLEvent parentEvent, String attrName, String attrValue) {
+ String message = "Unexpected attribute value %s='%s'";
+ message = String.format(message, attrName, attrValue);
+ IoProblemEvent event = makeProblemEvent(parentEvent.getLocation(), message, 1 , 1 );
+ fire(event);
+ }
+
+ protected void handleNotYetImplementedAttributeValue(XMLEvent xmlEvent, String attrName, String attrValue) {
+ String message = "Attribute %s not yet implemented for value '%s'";
+ message = String.format(message, attrName, attrValue);
+ IIoEvent event = makeProblemEvent(xmlEvent.getLocation(), message, 1, 1 );
+ fire(event);
+ }
+
+ protected void fireNotYetImplementedAttribute(Location location, QName qName, int stackDepth) {
+ String message = "Attribute not yet implemented: %s";
+ IIoEvent event = makeProblemEvent(location, String.format(message, qName.getLocalPart()), 1, stackDepth+1 );
+ fire(event);
+ }
+
+
+ protected void fireUnexpectedEvent(XMLEvent xmlEvent, int stackDepth) {
+ Location location = xmlEvent.getLocation();
+ String message = "Unexpected event: %s";
+ IIoEvent event = makeProblemEvent(location, String.format(message, xmlEvent.toString()), 2, stackDepth +1);
+ fire(event);
+ }
+
+ protected void fireUnexpectedStartElement(Location location, StartElement startElement, int stackDepth) {
+ QName qName = startElement.getName();
+ String message = "Unexpected start element: %s";
+ IIoEvent event = makeProblemEvent(location, String.format(message, qName.getLocalPart()), 2, stackDepth +1);
+ fire(event);
+ }
+
+
+ protected void fireUnexpectedEndElement(Location location, EndElement endElement, int stackDepth) {
+ QName qName = endElement.getName();
+ String message = "Unexpected end element: %s";
+ IIoEvent event = makeProblemEvent(location, String.format(message, qName.getLocalPart()), 16, stackDepth+1);
+ fire(event);
+ }
+
+ protected void fireNotYetImplementedElement(Location location, QName qName, int stackDepth) {
+ String message = "Element not yet implemented: %s";
+ IIoEvent event = makeProblemEvent(location, String.format(message, qName.getLocalPart()), 1, stackDepth+1 );
+ fire(event);
+ }
+
+ protected void fireNotYetImplementedCharacters(Location location, Characters chars, int stackDepth) {
+ String message = "Characters not yet handled: %s";
+ IIoEvent event = makeProblemEvent(location, String.format(message, chars.getData()), 1, stackDepth+1 );
+ fire(event);
+ }
+
+ /**
+ * Creates a problem event.
+ * Be aware of the right depths of the stack trace !
+ * @param location
+ * @param message
+ * @param severity
+ * @return
+ */
+ private IoProblemEvent makeProblemEvent(Location location, String message, int severity, int stackDepth) {
+ stackDepth++;
+ StackTraceElement[] stackTrace = new Exception().getStackTrace();
+ int lineNumber = stackTrace[stackDepth].getLineNumber();
+ String methodName = stackTrace[stackDepth].getMethodName();
+ String locationStr = makeLocationStr(location);
+ String className = stackTrace[stackDepth].getClassName();
+ Class<?> declaringClass;
+ try {
+ declaringClass = Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ declaringClass = this.getClass();
+ }
+ IoProblemEvent event = IoProblemEvent.NewInstance(declaringClass, message,
+ locationStr, lineNumber, severity, methodName);
+ return event;
+ }
+
+ /**
+ * Creates a string from a location
+ * @param location
+ * @return
+ */
+ protected String makeLocationStr(Location location) {
+ String locationStr = location == null ? " - no location - " : "l." + location.getLineNumber() + "/c."+ location.getColumnNumber();
+ return locationStr;
+ }
+
+
+ /**
+ * Fires an unexpected element event if the unhandled elements stack is empty.
+ * Otherwise adds the element to the stack.
+ * @param event
+ */
+ protected void handleUnexpectedStartElement(XMLEvent event) {
+ handleUnexpectedStartElement(event, 1);
+ }
+
+ /**
+ * Fires an unexpected element event if the unhandled elements stack is empty.
+ * Otherwise adds the element to the stack.
+ * @param event
+ */
+ protected void handleUnexpectedStartElement(XMLEvent event, int stackDepth) {
+ QName qName = event.asStartElement().getName();
+ if (! unhandledElements.empty()){
+ unhandledElements.push(qName);
+ }else{
+ fireUnexpectedStartElement(event.getLocation(), event.asStartElement(), stackDepth + 1);
+ }
+ }
+
+
+ protected void handleUnexpectedEndElement(EndElement event) {
+ handleUnexpectedEndElement(event, 1);
+ }
+
+ /**
+ * Fires an unexpected element event if the event is not the last on the stack.
+ * Otherwise removes last stack element.
+ * @param event
+ */
+ protected void handleUnexpectedEndElement(EndElement event, int stackDepth) {
+ QName qName = event.asEndElement().getName();
+ if (!unhandledElements.isEmpty() && unhandledElements.peek().equals(qName)){
+ unhandledElements.pop();
+ }else{
+ fireUnexpectedEndElement(event.getLocation(), event.asEndElement(), stackDepth + 1);
+ }
+ }
+
+ /**
+ *
+ * @param endElement
+ */
+ protected void popUnimplemented(EndElement endElement) {
+ QName qName = endElement.asEndElement().getName();
+ if (unhandledElements.peek().equals(qName)){
+ unhandledElements.pop();
+ }else{
+ String message = "End element is not last on stack: %s";
+ message = String.format(message, qName.getLocalPart());
+ IIoEvent event = makeProblemEvent(endElement.getLocation(), message, 16, 1);
+ fire(event);
+ }
+
+ }
+
+
+ /**
+ * Fires an unexpected element event if the unhandled element stack is empty.
+ * @param event
+ */
+ protected void handleUnexpectedElement(XMLEvent event) {
+ if (event.isStartElement()){
+ handleUnexpectedStartElement(event, 2);
+ }else if (event.isEndElement()){
+ handleUnexpectedEndElement(event.asEndElement(), 2);
+ }else if (event.getEventType() == XMLStreamConstants.COMMENT){
+ //do nothing
+ }else if (! unhandledElements.empty()){
+ //do nothing
+ }else{
+ fireUnexpectedEvent(event, 1);
+ }
+ }
+
+ /**
+ * Fires an not yet implemented event and adds the element name to the unhandled elements stack.
+ * @param event
+ */
+ protected void handleNotYetImplementedCharacters(XMLEvent event) {
+ Characters chars = event.asCharacters();
+ fireNotYetImplementedCharacters(event.getLocation(), chars, 1);
+ }
+
+ /**
+ * Fires an not yet implemented event and adds the element name to the unhandled elements stack.
+ * @param event
+ */
+ protected void handleNotYetImplementedElement(XMLEvent event) {
+ QName qName = event.asStartElement().getName();
+ boolean isTopLevel = unhandledElements.isEmpty();
+ unhandledElements.push(qName);
+ if (isTopLevel){
+ fireNotYetImplementedElement(event.getLocation(), qName, 1);
+ }
+ }
+
+ /**
+ * Fires an not yet implemented event and adds the element name to the unhandled elements stack.
+ * @param event
+ */
+ protected void handleIgnoreElement(XMLEvent event) {
+ QName qName = event.asStartElement().getName();
+ unhandledElements.push(qName);
+ }
+
+ protected void handleAmbigousManually(MarkupImportState state,
+ XMLEventReader reader, StartElement startElement) {
+ QName qName = startElement.getName();
+ unhandledElements.push(qName);
+ fireWarningEvent(
+ "Handle manually: " + qName.getLocalPart() + " is ambigous and should therefore be handled manually",
+ makeLocationStr(startElement.getLocation()), 2, 2);
+ }
+
+ /**
+ * Checks if a mandatory text is not empty or null.
+ * Returns true if text is given.
+ * Fires an mandatory element is missing event otherwise and returns <code>null</code>.
+ * @param text
+ * @param parentEvent
+ * @return
+ */
+ protected boolean checkMandatoryText(String text, XMLEvent parentEvent) {
+ if (! StringUtils.isNotBlank(text)){
+ fireMandatoryElementIsMissing(parentEvent, "CData", 4, 1);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Fires an mandatory element is missing event if exists is <code>false</code>.
+ * @param hasMandatory
+ * @param parentEvent
+ * @param string
+ */
+ protected void checkMandatoryElement(boolean exists, StartElement parentEvent, String attrName) {
+ if (! exists){
+ fireMandatoryElementIsMissing(parentEvent, attrName, 5, 1);
+ }
+ }
+
+
+ /**
+ * Fires an element is missing event.
+ * @param xmlEvent
+ * @param string
+ * @param severity
+ * @param stackDepth
+ * @throws IllegalStateException if xmlEvent is not a StartElement and not an Attribute
+ */
+ private void fireMandatoryElementIsMissing(XMLEvent xmlEvent, String missingEventName, int severity, int stackDepth) throws IllegalStateException{
+ Location location = xmlEvent.getLocation();
+ String typeName;
+ QName qName;
+ if (xmlEvent.isAttribute()){
+ Attribute attribute = ((Attribute)xmlEvent);
+ typeName = "attribute";
+ qName = attribute.getName();
+ }else if (xmlEvent.isStartElement()){
+ typeName = "element";
+ qName = xmlEvent.asStartElement().getName();
+ }else{
+ throw new IllegalStateException("mandatory element only allowed for attributes and start tags in " + makeLocationStr(location));
+ }
+ String message = "Mandatory %s '%s' is missing in %s";
+ message = String.format(message, typeName , missingEventName, qName.getLocalPart());
+ IIoEvent event = makeProblemEvent(location, message, severity, stackDepth +1);
+ fire(event);
+ }
+
+
+
+
+ /**
+ * Returns true if the "next" event is the ending tag for the "parent" event.
+ * @param next end element to test, must not be null
+ * @param parentEvent start element to test
+ * @return true if the "next" event is the ending tag for the "parent" event.
+ * @throws XMLStreamException
+ */
+ protected boolean isMyEndingElement(XMLEvent next, XMLEvent parentEvent) throws XMLStreamException {
+ if (! parentEvent.isStartElement()){
+ String message = "Parent event should be start tag";
+ fireWarningEvent(message, makeLocationStr(next.getLocation()), 6);
+ return false;
+ }
+ return isEndingElement(next, parentEvent.asStartElement().getName().getLocalPart());
+ }
+
+ /**
+ * Trims the text and removes turns all whitespaces into single empty space.
+ * @param text
+ * @return
+ */
+ protected String normalize(String text) {
+ text = StringUtils.trimToEmpty(text);
+ text = text.replaceAll("\\s+", " ");
+ return text;
+ }
+
+
+
+ /**
+ * Removes whitespaces at beginning and end and makes the first letter
+ * a capital letter and all other letters small letters.
+ * @param value
+ * @return
+ */
+ protected String toFirstCapital(String value) {
+ if (StringUtils.isBlank(value)){
+ return value;
+ }else{
+ String result = "";
+ value = value.trim();
+ result += value.trim().substring(0,1).toUpperCase();
+ if (value.length()>1){
+ result += value.substring(1).toLowerCase();
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Currently not used.
+ * @param str
+ * @param allowedNumberOfCharacters
+ * @param onlyFirstCapital
+ * @return
+ */
+ protected boolean isAbbreviation(String str, int allowedNumberOfCharacters, boolean onlyFirstCapital){
+ if (isBlank(str)){
+ return false;
+ }
+ str = str.trim();
+ if (! str.endsWith(".")){
+ return false;
+ }
+ str = str.substring(0, str.length() -1);
+ if (str.length() > allowedNumberOfCharacters){
+ return false;
+ }
+ final String re = "^\\p{javaUpperCase}\\p{javaLowerCase}*$";
+ if (str.matches(re)){
+ return true;
+ }else{
+ return false;
+ }
+ }
+
+ /**
+ * Checks if <code>abbrev</code> is the short form for the genus name (strGenusName).
+ * Usually this is the case if <code>abbrev</code> is the first letter (optional with ".")
+ * of strGenusName. But in older floras it may also be the first 2 or 3 letters (optional with dot).
+ * However, we allow only a maximum of 2 letters to be anambigous. In cases with 3 letters better
+ * change the original markup data.
+ * @param single
+ * @param strGenusName
+ * @return
+ */
+ protected boolean isGenusAbbrev(String abbrev, String strGenusName) {
+ if (! abbrev.matches("[A-Z][a-z]?\\.?")) {
+ return false;
+ }else if (abbrev.length() == 0 || strGenusName == null || strGenusName.length() == 0){
+ return false;
+ }else{
+ abbrev = abbrev.replace(".", "");
+ return strGenusName.startsWith(abbrev);
+// boolean result = true;
+// for (int i = 0 ; i < abbrev.length(); i++){
+// result &= ( abbrev.charAt(i) == strGenusName.charAt(i));
+// }
+// return result;
+ }
+ }
+
+
+ /**
+ * Checks if all words in the given string start with a capital letter but do not have any further capital letter.
+ * @param word the string to be checekd. Usually should be a single word.
+ * @return true if the above is the case, false otherwise
+ */
+ protected boolean isFirstCapitalWord(String word) {
+ if (WordUtils.capitalizeFully(word).equals(word)){
+ return true;
+ }else if (WordUtils.capitalizeFully(word,new char[]{'-'}).equals(word)){
+ //for words like Le-Testui (which is a species epithet)
+ return true;
+ }else{
+ return false;
+ }
+ }
+
+
+ /**
+ * Read next event. Ignore whitespace events.
+ * @param reader
+ * @return
+ * @throws XMLStreamException
+ */
+ protected XMLEvent readNoWhitespace(XMLEventReader reader) throws XMLStreamException {
+ XMLEvent event = reader.nextEvent();
+ while (!unhandledElements.isEmpty()){
+ if (event.isStartElement()){
+ handleNotYetImplementedElement(event);
+ }else if (event.isEndElement()){
+ popUnimplemented(event.asEndElement());
+ }
+ event = reader.nextEvent();
+ }
+ while (event.isCharacters() && event.asCharacters().isWhiteSpace()){
+ event = reader.nextEvent();
+ }
+ return event;
+ }
+
+ /**
+ * Returns the REQUIRED "class" attribute for a given event and checks that it is the only attribute.
+ * @param parentEvent
+ * @return
+ */
+ protected String getClassOnlyAttribute(XMLEvent parentEvent) {
+ return getClassOnlyAttribute(parentEvent, true);
+ }
+
+
+ /**
+ * Returns the "class" attribute for a given event and checks that it is the only attribute.
+ * @param parentEvent
+ * @return
+ */
+ protected String getClassOnlyAttribute(XMLEvent parentEvent, boolean required) {
+ return getOnlyAttribute(parentEvent, CLASS, required);
+ }
+
+ /**
+ * Returns the value for the only attribute for a given event and checks that it is the only attribute.
+ * @param parentEvent
+ * @return
+ */
+ protected String getOnlyAttribute(XMLEvent parentEvent, String attrName, boolean required) {
+ Map<String, Attribute> attributes = getAttributes(parentEvent);
+ String classValue =getAndRemoveAttributeValue(parentEvent, attributes, attrName, required, 1);
+ checkNoAttributes(attributes, parentEvent);
+ return classValue;
+ }
+
+
+ protected void fireWarningEvent(String message, String locationStr, Integer severity, Integer depth) {
+ docImport.fireWarningEvent(message, locationStr, severity, depth);
+ }
+
+ protected void fireWarningEvent(String message, XMLEvent event, Integer severity) {
+ docImport.fireWarningEvent(message, makeLocationStr(event.getLocation()), severity, 1);
+ }
+
+ protected void fireSchemaConflictEventExpectedStartTag(String elName, XMLEventReader reader) throws XMLStreamException {
+ docImport.fireSchemaConflictEventExpectedStartTag(elName, reader);
+ }
+
+
+ protected void fireWarningEvent(String message, String locationStr, int severity) {
+ docImport.fireWarningEvent(message, locationStr, severity, 1);
+ }
+
+ protected void fire(IIoEvent event) {
+ docImport.fire(event);
+ }
+
+ protected boolean isNotBlank(String str){
+ return StringUtils.isNotBlank(str);
+ }
+
+ protected boolean isBlank(String str){
+ return StringUtils.isBlank(str);
+ }
+
+ protected TaxonDescription getTaxonDescription(Taxon taxon, Reference ref, boolean isImageGallery, boolean createNewIfNotExists) {
+ return docImport.getTaxonDescription(taxon, isImageGallery, createNewIfNotExists);
+ }
+
+
+ /**
+ * Returns the default language defined in the state. If no default language is defined in the state,
+ * the CDM default language is returned.
+ * @param state
+ * @return
+ */
+ protected Language getDefaultLanguage(MarkupImportState state) {
+ Language result = state.getDefaultLanguage();
+ if (result == null){
+ result = Language.DEFAULT();
+ }
+ return result;
+ }
+
+
+//*********************** FROM XML IMPORT BASE ****************************************
+ protected boolean isEndingElement(XMLEvent event, String elName) throws XMLStreamException {
+ return docImport.isEndingElement(event, elName);
+ }
+
+ protected boolean isStartingElement(XMLEvent event, String elName) throws XMLStreamException {
+ return docImport.isStartingElement(event, elName);
+ }
+
+
+ protected void fillMissingEpithetsForTaxa(Taxon parentTaxon, Taxon childTaxon) {
+ docImport.fillMissingEpithetsForTaxa(parentTaxon, childTaxon);
+ }
+
+ protected Feature getFeature(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<Feature> voc){
+ return docImport.getFeature(state, uuid, label, text, labelAbbrev, voc);
+ }
+
+ protected PresenceAbsenceTerm getPresenceAbsenceTerm(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, boolean isAbsenceTerm, TermVocabulary<PresenceAbsenceTerm> voc){
+ return docImport.getPresenceTerm(state, uuid, label, text, labelAbbrev, isAbsenceTerm, voc);
+ }
+
+ protected ExtensionType getExtensionType(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev){
+ return docImport.getExtensionType(state, uuid, label, text, labelAbbrev);
+ }
+
+ protected DefinedTerm getIdentifierType(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<DefinedTerm> voc){
+ return docImport.getIdentifierType(state, uuid, label, text, labelAbbrev, voc);
+ }
+
+ protected AnnotationType getAnnotationType(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<AnnotationType> voc){
+ return docImport.getAnnotationType(state, uuid, label, text, labelAbbrev, voc);
+ }
+
+ protected MarkerType getMarkerType(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<MarkerType> voc){
+ return docImport.getMarkerType(state, uuid, label, text, labelAbbrev, voc);
+ }
+
+ protected NamedAreaLevel getNamedAreaLevel(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<NamedAreaLevel> voc){
+ return docImport.getNamedAreaLevel(state, uuid, label, text, labelAbbrev, voc);
+ }
+
+ protected NamedArea getNamedArea(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level, TermVocabulary voc, TermMatchMode matchMode){
+ return docImport.getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level, voc, matchMode);
+ }
+
+ protected Language getLanguage(MarkupImportState state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<?> voc){
+ return docImport.getLanguage(state, uuid, label, text, labelAbbrev, voc);
+ }
+
+// *************************************** Concrete methods **********************************************/
+
+
+ /**
+ * @param state
+ * @param classValue
+ * @param byAbbrev
+ * @return
+ */
+ protected Rank makeRank(MarkupImportState state, String value, boolean byAbbrev) {
+ Rank rank = null;
+ if (StringUtils.isBlank(value)) {
+ return null;
+ }
+ try {
+ boolean useUnknown = true;
+ NomenclaturalCode nc = makeNomenclaturalCode(state);
+ if (value.equals(GENUS_ABBREVIATION)){
+ rank = Rank.GENUS();
+ }else if (byAbbrev) {
+ rank = Rank.getRankByIdInVoc(value.toLowerCase(), nc, useUnknown);
+ if (value.equalsIgnoreCase("forma")){
+ return Rank.FORM();
+ }else if (value.toLowerCase().matches("(sub)?(section|genus|series|tribe)")){
+ return Rank.getRankByEnglishName(value, nc, useUnknown);
+ }else if (value.equals("§")){
+ return Rank.SECTION_BOTANY(); //Special case in Flora Malesiana
+ }
+ } else {
+ rank = Rank.getRankByEnglishName(value, nc, useUnknown);
+ }
+ if (rank.equals(Rank.UNKNOWN_RANK())) {
+ rank = null;
+ }
+ if (rank == null && "sous-genre".equalsIgnoreCase(value)){
+ rank = Rank.SUBGENUS();
+ }
+ } catch (UnknownCdmTypeException e) {
+ // doNothing
+ }
+ return rank;
+ }
+
+
+
+ protected TeamOrPersonBase<?> createAuthor(String authorTitle) {
+ // TODO atomize and also use by name creation
+ TeamOrPersonBase<?> result = Team.NewTitledInstance(authorTitle, authorTitle);
+ return result;
+ }
+
+ protected String getAndRemoveMapKey(Map<String, String> map, String key) {
+ String result = map.get(key);
+ map.remove(key);
+ if (result != null) {
+ result = normalize(result);
+ }
+ return StringUtils.stripToNull(result);
+ }
+
+
+ /**
+ * Creates a {@link NonViralName} object depending on the defined {@link NomenclaturalCode}
+ * and the given parameters.
+ * @param state
+ * @param rank
+ * @return
+ */
+ protected NonViralName<?> createNameByCode(MarkupImportState state, Rank rank) {
+ NonViralName<?> name;
+ NomenclaturalCode nc = makeNomenclaturalCode(state);
+ name = (NonViralName<?>) nc.getNewTaxonNameInstance(rank);
+ return name;
+ }
+
+ protected void handleFullName(MarkupImportState state, XMLEventReader reader,
+ INonViralName name, XMLEvent next) throws XMLStreamException {
+ String fullNameStr;
+ Map<String, Attribute> attrs = getAttributes(next);
+ String rankStr = getAndRemoveRequiredAttributeValue(next,
+ attrs, "rank");
+ Rank rank = makeRank(state, rankStr, false);
+ name.setRank(rank);
+ if (rank == null) {
+ String message = "Rank was computed as null. This must not be.";
+ fireWarningEvent(message, next, 6);
+ name.setRank(Rank.UNKNOWN_RANK());
+ }
+ if (!attrs.isEmpty()) {
+ handleUnexpectedAttributes(next.getLocation(), attrs);
+ }
+// next = readNoWhitespace(reader);
+ fullNameStr = getCData(state, reader, next, false);
+ NonViralNameParserImpl.NewInstance().parseFullName(name, fullNameStr, rank, false);
+// name.setTitleCache(fullNameStr, true);
+ }
+
+
+ /**
+ * Returns the {@link NomenclaturalCode} for this import. Default is {@link NomenclaturalCode#ICBN} if
+ * no code is defined.
+ * @param state
+ * @return
+ */
+ protected NomenclaturalCode makeNomenclaturalCode(MarkupImportState state) {
+ NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
+ if (nc == null) {
+ nc = NomenclaturalCode.ICNAFP; // default;
+ }
+ return nc;
+ }
+
+
+ /**
+ * @param state
+ * @param levelString
+ * @param next
+ * @return
+ */
+ protected NamedAreaLevel makeNamedAreaLevel(MarkupImportState state, String levelString, XMLEvent next) {
+ NamedAreaLevel level;
+ try {
+ level = state.getTransformer().getNamedAreaLevelByKey(levelString);
+ if (level == null) {
+ UUID levelUuid = state.getTransformer().getNamedAreaLevelUuid(levelString);
+ if (levelUuid == null) {
+ String message = "Unknown distribution locality class (named area level): %s. Create new level instead.";
+ message = String.format(message, levelString);
+ fireWarningEvent(message, next, 6);
+ }
+ level = getNamedAreaLevel(state, levelUuid, levelString, levelString, levelString, null);
+ }
+ } catch (UndefinedTransformerMethodException e) {
+ throw new RuntimeException(e);
+ }
+ return level;
+ }
+
+
+ /**
+ * @param state
+ * @param areaName
+ * @param level
+ * @return
+ */
+ protected NamedArea makeArea(MarkupImportState state, String areaName, NamedAreaLevel level) {
+
+ //TODO FM vocabulary
+ TermVocabulary<NamedArea> voc = null;
+ NamedAreaType areaType = null;
+
+ NamedArea area = null;
+ try {
+ area = state.getTransformer().getNamedAreaByKey(areaName);
+ } catch (UndefinedTransformerMethodException e) {
+ throw new RuntimeException(e);
+ }
+ if (area == null){
+ boolean isNewInState = false;
+ UUID uuid = state.getAreaUuid(areaName);
+ if (uuid == null){
+ isNewInState = true;
+
+
+ try {
+ uuid = state.getTransformer().getNamedAreaUuid(areaName);
+ } catch (UndefinedTransformerMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ CdmImportBase.TermMatchMode matchMode = CdmImportBase.TermMatchMode.UUID_LABEL;
+ area = getNamedArea(state, uuid, areaName, areaName, areaName, areaType, level, voc, matchMode);
+ if (isNewInState){
+ state.putAreaUuid(areaName, area.getUuid());
+
+ //TODO just for testing -> make generic and move to better place
+ String geoServiceLayer="vmap0_as_bnd_political_boundary_a";
+ String layerFieldName ="nam";
+
+ if ("Bangka".equals(areaName)){
+ String areaValue = "PULAU BANGKA#SUMATERA SELATAN";
+ GeoServiceArea geoServiceArea = new GeoServiceArea();
+ geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);
+ this.editGeoService.setMapping(area, geoServiceArea);
+// save(area, state);
+ }
+ if ("Luzon".equals(areaName)){
+ GeoServiceArea geoServiceArea = new GeoServiceArea();
+
+ List<String> list = Arrays.asList("HERMANA MAYOR ISLAND#CENTRAL LUZON",
+ "HERMANA MENOR ISLAND#CENTRAL LUZON",
+ "CENTRAL LUZON");
+ for (String areaValue : list){
+ geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);
+ }
+
+ this.editGeoService.setMapping(area, geoServiceArea);
+// save(area, state);
+ }
+ if ("Mindanao".equals(areaName)){
+ GeoServiceArea geoServiceArea = new GeoServiceArea();
+
+ List<String> list = Arrays.asList("NORTHERN MINDANAO",
+ "SOUTHERN MINDANAO",
+ "WESTERN MINDANAO");
+ //TODO to be continued
+ for (String areaValue : list){
+ geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);
+ }
+
+ this.editGeoService.setMapping(area, geoServiceArea);
+// save(area, state);
+ }
+ if ("Palawan".equals(areaName)){
+ GeoServiceArea geoServiceArea = new GeoServiceArea();
+
+ List<String> list = Arrays.asList("PALAWAN#SOUTHERN TAGALOG");
+ for (String areaValue : list){
+ geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);
+ }
+
+ this.editGeoService.setMapping(area, geoServiceArea);
+// save(area, state);
+ }
+
+ }
+ }
+ return area;
+ }
+
+
+
+ /**
+ * Reads character data. Any element other than character data or the ending
+ * tag will fire an unexpected element event.
+ *
+ * @see #getCData(MarkupImportState, XMLEventReader, XMLEvent, boolean)
+ * @param state
+ * @param reader
+ * @param next
+ * @return
+ * @throws XMLStreamException
+ */
+ protected String getCData(MarkupImportState state, XMLEventReader reader, XMLEvent next) throws XMLStreamException {
+ return getCData(state, reader, next, true);
+ }
+
+ /**
+ * Reads character data. Any element other than character data or the ending
+ * tag will fire an unexpected element event.
+ *
+ * @param state
+ * @param reader
+ * @param next
+ * @param inlineMarkup map for inline markup, this is used for e.g. the locality markup within a subheading
+ * The map will be filled by the markup element name as key. The value may be a String, a CdmBase or any other object.
+ * If null any markup text will be neglected but a warning will be fired if they exist.
+ * @param removeInlineMarkupText if true the markedup text will be removed from the returned String
+ * @param checkAttributes
+ * @return
+ * @throws XMLStreamException
+ */
+ protected String getCData(MarkupImportState state, XMLEventReader reader, XMLEvent parent, /*Map<String, Object> inlineMarkup, *boolean removeInlineMarkupText,*/ boolean checkAttributes) throws XMLStreamException {
+ if (checkAttributes){
+ checkNoAttributes(parent);
+ }
+
+ String text = "";
+ while (reader.hasNext()) {
+ XMLEvent next = readNoWhitespace(reader);
+ if (isMyEndingElement(next, parent)) {
+ return text;
+ } else if (next.isCharacters()) {
+ text += next.asCharacters().getData();
+ } else if (isStartingElement(next, FOOTNOTE_REF)){
+ handleNotYetImplementedElement(next);
+// } else if (isStartingElement(next, LOCALITY)){
+// handleCDataLocality(state, reader, parent);
+ } else {
+ handleUnexpectedElement(next);
+ }
+ }
+ throw new IllegalStateException("Event has no closing tag");
+
+ }
+
+// private void handleCDataLocality(MarkupImportState state, XMLEventReader reader, XMLEvent parent) {
+// checkAndRemoveAttributeValue(attributes, attrName, value)
+//
+// }
+
+
+
+ /**
+ * For it returns a pure CData annotation string. This behaviour may change in future. More complex annotations
+ * should be handled differently.
+ * @param state
+ * @param reader
+ * @param parentEvent
+ * @return
+ * @throws XMLStreamException
+ */
+ protected String handleSimpleAnnotation(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
+ String annotation = getCData(state, reader, parentEvent);
+ return annotation;
+ }
+
+ /**
+ * True if text is single "." oder "," or ";" or ":"
+ * @param text
+ * @return
+ */
+ protected boolean isPunctuation(String text) {
+ return text == null ? false : text.trim().matches("^[\\.,;:]$");
+ }
+
+
+ /**
+ * Text indicating that type information is following but no information about the type of the type
+ * @param text
+ * @return
+ */
+ protected boolean charIsSimpleType(String text) {
+ return text.matches("(?i)Type:");
+ }
+
+ protected String getXmlTag(XMLEvent event) {
+ String result;
+ if (event.isStartElement()) {
+ result = "<" + event.asStartElement().getName().getLocalPart()
+ + ">";
+ } else if (event.isEndElement()) {
+ result = "</" + event.asEndElement().getName().getLocalPart() + ">";
+ } else {
+ String message = "Only start or end elements are allowed as Html tags";
+ throw new IllegalStateException(message);
+ }
+ return result;
+ }
+
+ protected WriterDataHolder handleWriter(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
+ String text = "";
+ checkNoAttributes(parentEvent);
+ WriterDataHolder dataHolder = new WriterDataHolder();
+ List<FootnoteDataHolder> footnotes = new ArrayList<FootnoteDataHolder>();
+
+ // TODO handle attributes
+ while (reader.hasNext()) {
+ XMLEvent next = readNoWhitespace(reader);
+ if (isMyEndingElement(next, parentEvent)) {
+ text = CdmUtils.removeBrackets(text);
+ if (checkMandatoryText(text, parentEvent)) {
+ text = normalize(text);
+ dataHolder.writer = text;
+ dataHolder.footnotes = footnotes;
+
+ // Extension
+ UUID uuidWriterExtension = MarkupTransformer.uuidWriterExtension;
+ ExtensionType writerExtensionType =
+ this.getExtensionType(state, uuidWriterExtension,"Writer", "writer", "writer");
+ Extension extension = Extension.NewInstance();
+ extension.setType(writerExtensionType);
+ extension.setValue(text);
+ dataHolder.extension = extension;
+
+ // Annotation
+ UUID uuidWriterAnnotation = MarkupTransformer.uuidWriterAnnotation;
+ AnnotationType writerAnnotationType = this.getAnnotationType(state, uuidWriterAnnotation, "Writer", "writer", "writer", null);
+ Annotation annotation = Annotation.NewInstance(text, writerAnnotationType, getDefaultLanguage(state));
+ dataHolder.annotation = annotation;
+
+ return dataHolder;
+ } else {
+ return null;
+ }
+ } else if (isStartingElement(next, FOOTNOTE_REF)) {
+ FootnoteDataHolder footNote = handleFootnoteRef(state, reader, next);
+ if (footNote.isRef()) {
+ footnotes.add(footNote);
+ } else {
+ logger.warn("Non ref footnotes not yet impelemnted");
+ }
+ } else if (next.isCharacters()) {
+ text += next.asCharacters().getData();
+
+ } else {
+ handleUnexpectedElement(next);
+ state.setUnsuccessfull();
+ }
+ }
+ throw new IllegalStateException("<writer> has no end tag");
+ }
+
+
+ protected void registerFootnotes(MarkupImportState state, AnnotatableEntity entity, List<FootnoteDataHolder> footnotes) {
+ for (FootnoteDataHolder footNote : footnotes) {
+ registerFootnoteDemand(state, entity, footNote);
+ }
+ }
+
+
+ private void registerFootnoteDemand(MarkupImportState state, AnnotatableEntity entity, FootnoteDataHolder footnote) {
+ FootnoteDataHolder existingFootnote = state.getFootnote(footnote.ref);
+ if (existingFootnote != null) {
+ attachFootnote(state, entity, existingFootnote);
+ } else {
+ Set<AnnotatableEntity> demands = state.getFootnoteDemands(footnote.ref);
+ if (demands == null) {
+ demands = new HashSet<AnnotatableEntity>();
+ state.putFootnoteDemands(footnote.ref, demands);
+ }
+ demands.add(entity);
+ }
+ }
+
+
+ protected void attachFootnote(MarkupImportState state, AnnotatableEntity entity, FootnoteDataHolder footnote) {
+ AnnotationType annotationType = this.getAnnotationType(state, MarkupTransformer.uuidFootnote, "Footnote", "An e-flora footnote", "fn", null);
+ Annotation annotation = Annotation.NewInstance(footnote.string, annotationType, getDefaultLanguage(state));
+ // TODO transient objects
+ entity.addAnnotation(annotation);
+ save(entity, state);
+ }
+
+
+ protected void attachFigure(MarkupImportState state, XMLEvent next, AnnotatableEntity entity, Media figure) {
+ // IdentifiableEntity<?> toSave;
+ if (entity.isInstanceOf(TextData.class)) {
+ TextData deb = CdmBase.deproxy(entity, TextData.class);
+ deb.addMedia(figure);
+ // toSave = ((TaxonDescription)deb.getInDescription()).getTaxon();
+ } else if (entity.isInstanceOf(SpecimenOrObservationBase.class)) {
+ String message = "figures for specimen should be handled as Textdata";
+ fireWarningEvent(message, next, 4);
+ // toSave = ime;
+ } else if (entity.isInstanceOf(IdentifiableMediaEntity.class)) {
+ IdentifiableMediaEntity<?> ime = CdmBase.deproxy(entity, IdentifiableMediaEntity.class);
+ ime.addMedia(figure);
+ // toSave = ime;
+ } else {
+ String message = "Unsupported entity to attach media: %s";
+ message = String.format(message, entity.getClass().getName());
+ // toSave = null;
+ }
+ save(entity, state);
+ }
+
+
+ protected void registerGivenFootnote(MarkupImportState state, FootnoteDataHolder footnote) {
+ state.registerFootnote(footnote);
+ Set<AnnotatableEntity> demands = state.getFootnoteDemands(footnote.id);
+ if (demands != null) {
+ for (AnnotatableEntity entity : demands) {
+ attachFootnote(state, entity, footnote);
+ }
+ }
+ }
+
+
+ protected FootnoteDataHolder handleFootnote(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent,
+ MarkupSpecimenImport specimenImport, MarkupNomenclatureImport nomenclatureImport) throws XMLStreamException {
+ FootnoteDataHolder result = new FootnoteDataHolder();
+ Map<String, Attribute> attributes = getAttributes(parentEvent);
+ result.id = getAndRemoveAttributeValue(attributes, ID);
+ // result.ref = getAndRemoveAttributeValue(attributes, REF);
+ checkNoAttributes(attributes, parentEvent);
+
+ while (reader.hasNext()) {
+ XMLEvent next = readNoWhitespace(reader);
+ if (isStartingElement(next, FOOTNOTE_STRING)) {
+ String string = handleFootnoteString(state, reader, next, specimenImport, nomenclatureImport);
+ result.string = string;
+ } else if (isMyEndingElement(next, parentEvent)) {
+ return result;
+ } else {
+ fireUnexpectedEvent(next, 0);
+ }
+ }
+ return result;
+ }
+
+
+ protected Media handleFigure(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent,
+ MarkupSpecimenImport specimenImport, MarkupNomenclatureImport nomenclatureImport) throws XMLStreamException {
+ // FigureDataHolder result = new FigureDataHolder();
+
+ Map<String, Attribute> attributes = getAttributes(parentEvent);
+ String id = getAndRemoveAttributeValue(attributes, ID);
+ String type = getAndRemoveAttributeValue(attributes, TYPE);
+ String urlAttr = getAndRemoveAttributeValue(attributes, URL);
+ checkNoAttributes(attributes, parentEvent);
+
+ String urlString = null;
+ String legendString = null;
+ String titleString = null;
+ String numString = null;
+ String text = null;
+ if (isNotBlank(urlAttr)){
+ urlString = CdmUtils.Nz(state.getBaseMediaUrl()) + urlAttr;
+ }
+ while (reader.hasNext()) {
+ XMLEvent next = readNoWhitespace(reader);
+ if (isMyEndingElement(next, parentEvent)) {
+ if (isNotBlank(text)){
+ if (isNeglectableFigureText(text)){
+ fireWarningEvent("Text not yet handled for figures: " + text, next, 4);
+ }
+ }
+ Media media = makeFigure(state, id, type, urlString, legendString, titleString, numString, next);
+ return media;
+ } else if (isStartingElement(next, FIGURE_LEGEND)) {
+ // TODO same as figure string ?
+ legendString = handleFootnoteString(state, reader, next, specimenImport, nomenclatureImport);
+ } else if (isStartingElement(next, FIGURE_TITLE)) {
+ titleString = getCData(state, reader, next);
+ } else if (isStartingElement(next, URL)) {
+ String localUrl = getCData(state, reader, next);
+ String url = CdmUtils.Nz(state.getBaseMediaUrl()) + localUrl;
+ if (isBlank(urlString)){
+ urlString = url;
+ }
+ if (! url.equals(urlString)){
+ String message = "URL attribute and URL element differ. Attribute: %s, Element: %s";
+ fireWarningEvent(String.format(message, urlString, url), next, 2);
+ }
+ } else if (isStartingElement(next, NUM)) {
+ numString = getCData(state, reader, next);
+ } else if (next.isCharacters()) {
+ text = CdmUtils.concat("", text, next.asCharacters().getData());
+ } else {
+ fireUnexpectedEvent(next, 0);
+ }
+ }
+ throw new IllegalStateException("<figure> has no end tag");
+ }
+
+
+ /**
+ * @param text2
+ * @return
+ */
+ private boolean isNeglectableFigureText(String text) {
+ if (text.matches("Fig\\.*")){
+ return true;
+ }else{
+ return false;
+ }
+ }
+
+
+ /**
+ * @param state
+ * @param id
+ * @param type
+ * @param urlString
+ * @param legendString
+ * @param titleString
+ * @param numString
+ * @param next
+ */
+ private Media makeFigure(MarkupImportState state, String id, String type, String urlString,
+ String legendString, String titleString, String numString, XMLEvent next) {
+ Media media = null;
+// boolean isFigure = false; //no difference between figure and media since v3.3
+ try {
+ //TODO maybe everything is a figure as it is all taken from a book
+ if ("lineart".equals(type)) {
+// isFigure = true;
+// media = Figure.NewInstance(url.toURI(), null, null, null);
+ } else if (type == null || "photo".equals(type)
+ || "signature".equals(type)
+ || "others".equals(type)) {
+ //TODO
+ } else {
+ String message = "Unknown figure type '%s'";
+ message = String.format(message, type);
+ fireWarningEvent(message, next, 2);
+ }
+ media = docImport.getImageMedia(urlString, docImport.getReadMediaData());
+
+ if (media != null){
+ // title
+ if (StringUtils.isNotBlank(titleString)) {
+ media.putTitle(getDefaultLanguage(state), titleString);
+ }
+ // legend
+ if (StringUtils.isNotBlank(legendString)) {
+ media.putDescription(getDefaultLanguage(state), legendString);
+ }
+ if (StringUtils.isNotBlank(numString)) {
+ // TODO use concrete source (e.g. DAPHNIPHYLLACEAE in FM
+ // vol.13)
+ Reference citation = state.getConfig().getSourceReference();
+ media.addSource(OriginalSourceType.Import, numString, "num", citation, null);
+ // TODO name used in source if available
+ }
+ // TODO which citation
+ if (StringUtils.isNotBlank(id)) {
+ media.addSource(OriginalSourceType.Import, id, null, state.getConfig().getSourceReference(), null);
+ } else {
+ String message = "Figure id should never be empty or null";
+ fireWarningEvent(message, next, 6);
+ }
+
+ // text
+ // do nothing
+ registerGivenFigure(state, next, id, media);
+
+ }else{
+ String message = "No media found: ";
+ fireWarningEvent(message, next, 4);
+ }
+ } catch (MalformedURLException e) {
+ String message = "Media uri has incorrect syntax: %s";
+ message = String.format(message, urlString);
+ fireWarningEvent(message, next, 4);
+// } catch (URISyntaxException e) {
+// String message = "Media uri has incorrect syntax: %s";
+// message = String.format(message, urlString);
+// fireWarningEvent(message, next, 4);
+ }
+
+ return media;
+ }
+
+
+ private void registerGivenFigure(MarkupImportState state, XMLEvent next, String id, Media figure) {
+ state.registerFigure(id, figure);
+ Set<AnnotatableEntity> demands = state.getFigureDemands(id);
+ if (demands != null) {
+ for (AnnotatableEntity entity : demands) {
+ attachFigure(state, next, entity, figure);
+ }
+ }
+ save(figure, state);
+ }
+
+
+ private FootnoteDataHolder handleFootnoteRef(MarkupImportState state,
+ XMLEventReader reader, XMLEvent parentEvent)
+ throws XMLStreamException {
+ FootnoteDataHolder result = new FootnoteDataHolder();
+ Map<String, Attribute> attributes = getAttributes(parentEvent);
+ result.ref = getAndRemoveAttributeValue(attributes, REF);
+ checkNoAttributes(attributes, parentEvent);
+
+ // text is not handled, needed only for debugging purposes
+ String text = "";
+ while (reader.hasNext()) {
+ XMLEvent next = readNoWhitespace(reader);
+ // if (isStartingElement(next, FOOTNOTE_STRING)){
+ // String string = handleFootnoteString(state, reader, next);
+ // result.string = string;
+ // }else
+ if (isMyEndingElement(next, parentEvent)) {
+ if (StringUtils.isNotBlank(text)){
+ fireWarningEvent("text is not empty but not handled during import", parentEvent, 4);
+ }
+ return result;
+ } else if (next.isCharacters() && unhandledElements.isEmpty()) {
+ text += next.asCharacters().getData();
+ } else if (isStartingElement(next, NUM)) {
+ //ignore numbering of footnotes as they are numbered differently in the CDM
+ handleIgnoreElement(next);
+ } else {
+ handleUnexpectedElement(next);
+ }
+ }
+ return result;
+ }
+
+
+
+ private String handleFootnoteString(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, MarkupSpecimenImport specimenImport, MarkupNomenclatureImport nomenclatureImport) throws XMLStreamException {
+ boolean isTextMode = true;
+ String text = "";
+ while (reader.hasNext()) {
+ XMLEvent next = readNoWhitespace(reader);
+ if (isMyEndingElement(next, parentEvent)) {
+ return text;
+ } else if (next.isEndElement()) {
+ if (isEndingElement(next, FULL_NAME)) {
+ popUnimplemented(next.asEndElement());
+ } else if (isEndingElement(next, BR)) {
+ isTextMode = true;
+ } else if (isHtml(next)) {
+ text += getXmlTag(next);
+ } else {
+ handleUnexpectedEndElement(next.asEndElement());
+ }
+ } else if (next.isStartElement()) {
+ if (isStartingElement(next, FULL_NAME)) {
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, GATHERING)) {
+ text += specimenImport.handleInLineGathering(state, reader, next);
+ } else if (isStartingElement(next, REFERENCES)) {
+ text += " " + handleInLineReferences(state, reader, next, nomenclatureImport)+ " ";
+ } else if (isStartingElement(next, BR)) {
+ text += "<br/>";
+ isTextMode = false;
+ } else if (isStartingElement(next, NOMENCLATURE)) {
+ handleNotYetImplementedElement(next);
+ } else if (isHtml(next)) {
+ text += getXmlTag(next);
+ } else {
+ handleUnexpectedStartElement(next.asStartElement());
+ }
+ } else if (next.isCharacters()) {
+ if (!isTextMode) {
+ String message = "footnoteString is not in text mode";
+ fireWarningEvent(message, next, 6);
+ } else {
+ text += next.asCharacters().getData().trim();
+ // getCData(state, reader, next); does not work as we have inner tags like <references>
+ }
+ } else {
+ handleUnexpectedEndElement(next.asEndElement());
+ }
+ }
+ throw new IllegalStateException("<footnoteString> has no closing tag");
+
+ }
+
+ private static final List<String> htmlList = Arrays.asList("sub", "sup",
+ "ol", "ul", "li", "i", "b", "table", "br","tr","td","th");
+
+ protected boolean isHtml(XMLEvent event) {
+ if (event.isStartElement()) {
+ String tag = event.asStartElement().getName().getLocalPart();
+ return htmlList.contains(tag);
+ } else if (event.isEndElement()) {
+ String tag = event.asEndElement().getName().getLocalPart();
+ return htmlList.contains(tag);
+ } else {
+ return false;
+ }
+
+ }
+
+
+ private String handleInLineReferences(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent, MarkupNomenclatureImport nomenclatureImport) throws XMLStreamException {
+ checkNoAttributes(parentEvent);
+
+ boolean hasReference = false;
+ String text = "";
+ while (reader.hasNext()) {
+ XMLEvent next = readNoWhitespace(reader);
+ if (isMyEndingElement(next, parentEvent)) {
+ checkMandatoryElement(hasReference, parentEvent.asStartElement(), REFERENCE);
+ return text;
+ } else if (isStartingElement(next, REFERENCE)) {
+ text += handleInLineReference(state, reader, next, nomenclatureImport);
+ hasReference = true;
+ } else {
+ handleUnexpectedElement(next);
+ }
+ }
+ throw new IllegalStateException("<References> has no closing tag");
+ }
+
+ private String handleInLineReference(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent, MarkupNomenclatureImport nomenclatureImport)throws XMLStreamException {
+ Reference reference = nomenclatureImport.handleReference(state, reader, parentEvent);
+ String result = "<cdm:ref uuid='%s'>%s</ref>";
+ result = String.format(result, reference.getUuid(), reference.getTitleCache());
+ save(reference, state);
+ return result;
+ }
+
+
+ /**
+ * Handle string
+ * @param state
+ * @param reader
+ * @param parentEvent
+ * @param feature only needed for distributionLocalities
+ * @return
+ * @throws XMLStreamException
+ */
+ protected Map<String, String> handleString(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, Feature feature)throws XMLStreamException {
+ // attributes
+ String classValue = getClassOnlyAttribute(parentEvent, false);
+ if (StringUtils.isNotBlank(classValue)) {
+ String message = "class attribute for <string> not yet implemented";
+ fireWarningEvent(message, parentEvent, 2);
+ }
+ boolean isHabitat = false;
+
+ // subheadings
+ Map<String, String> subHeadingMap = new HashMap<String, String>();
+ String currentSubheading = null;
+
+ boolean isTextMode = true;
+ String text = "";
+ while (reader.hasNext()) {
+ XMLEvent next = readNoWhitespace(reader);
+ if (isMyEndingElement(next, parentEvent)) {
+ putCurrentSubheading(subHeadingMap, currentSubheading, text);
+ if (isHabitat ){
+ if (currentSubheading != null && ! isHabitatHeading(currentSubheading) || ! isBlankOrPunctuation(text)){
+ String message = "String is habitat but currentSubHeading or text is not blank: " + CdmUtils.concat(": ", currentSubheading, text);
+ fireWarningEvent(message, next, 4);
+ }
+ }
+ return subHeadingMap;
+ } else if (isStartingElement(next, BR)) {
+ text += "<br/>";
+ isTextMode = false;
+ } else if (isEndingElement(next, BR)) {
+ isTextMode = true;
+ } else if (isHtml(next)) {
+ text += getXmlTag(next);
+ } else if (isStartingElement(next, SUB_HEADING)) {
+ text = putCurrentSubheading(subHeadingMap,currentSubheading, text);
+ // TODO footnotes
+ currentSubheading = getCData(state, reader, next).trim();
+ } else if (isStartingElement(next, DISTRIBUTION_LOCALITY)) {
+ if (feature != null && !feature.equals(Feature.DISTRIBUTION())) {
+ String message = "Distribution locality only allowed for feature of type 'distribution'";
+ fireWarningEvent(message, next, 4);
+ }
+ text += handleDistributionLocality(state, reader, next);
+ } else if (next.isCharacters()) {
+ if (! isTextMode) {
+ String message = "String is not in text mode";
+ fireWarningEvent(message, next, 6);
+ } else {
+ text += next.asCharacters().getData();
+ }
+ } else if (isStartingElement(next, HEADING)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, VERNACULAR_NAMES)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, QUOTE)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, DEDICATION)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, TAXONTYPE)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, FULL_NAME)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ }else if (isStartingElement(next, REFERENCES)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, GATHERING)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, ANNOTATION)) {
+ //TODO //TODO test handleSimpleAnnotation
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, HABITAT)) {
+ featureImport.handleHabitat(state, reader, next);
+ isHabitat = true;
+ } else if (isStartingElement(next, FIGURE_REF)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, FIGURE)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, FOOTNOTE_REF)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, FOOTNOTE)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, WRITER)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isStartingElement(next, DATES)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else {
+ handleUnexpectedElement(next);
+ }
+ }
+ throw new IllegalStateException("<String> has no closing tag");
+ }
+
+
+ /**
+ * @param text2
+ * @return
+ */
+ private boolean isBlankOrPunctuation(String text) {
+ if (text == null){
+ return true;
+ } else {
+ return text.matches("^[\\s\\.,;:]*$");
+ }
+ }
+
+
+ /**
+ * @param currentSubheading
+ * @return
+ */
+ private boolean isHabitatHeading(String heading) {
+ return heading.trim().matches("(Ecol(ogy)?|Habitat|Habitat\\s&\\sEcology)\\.?");
+ }
+
+
+ /**
+ * @param subHeadingMap
+ * @param currentSubheading
+ * @param text
+ * @return
+ */
+ private String putCurrentSubheading(Map<String, String> subHeadingMap, String currentSubheading, String text) {
+ if (StringUtils.isNotBlank(text)) {
+ text = removeStartingMinus(text);
+ subHeadingMap.put(currentSubheading, text.trim());
+ }
+ return "";
+ }
+
+ private String removeStartingMinus(String string) {
+ string = replaceStart(string, "-");
+ string = replaceStart(string, "\u002d");
+ string = replaceStart(string, "\u2013");
+ string = replaceStart(string, "\u2014");
+ string = replaceStart(string, "--");
+ return string;
+ }
+
+
+ /**
+ * @param value
+ * @param replacementString
+ */
+ private String replaceStart(String value, String replacementString) {
+ if (value.startsWith(replacementString) ){
+ value = value.substring(replacementString.length()).trim();
+ }
+ while (value.startsWith("-") || value.startsWith("\u2014") ){
+ value = value.substring("-".length()).trim();
+ }
+ return value;
+ }
+
+
+ private String handleDistributionLocality(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent)throws XMLStreamException {
+ Map<String, Attribute> attributes = getAttributes(parentEvent);
+ String classValue = getAndRemoveRequiredAttributeValue(parentEvent, attributes, CLASS);
+ String statusValue =getAndRemoveAttributeValue(attributes, STATUS);
+ String frequencyValue =getAndRemoveAttributeValue(attributes, FREQUENCY);
+
+
+ Taxon taxon = state.getCurrentTaxon();
+ // TODO which ref to take?
+ Reference ref = state.getConfig().getSourceReference();
+
+ String text = "";
+ while (reader.hasNext()) {
+ XMLEvent next = readNoWhitespace(reader);
+ if (isMyEndingElement(next, parentEvent)) {
+ if (StringUtils.isNotBlank(text)) {
+ String label = CdmUtils.removeTrailingDot(normalize(text));
+ TaxonDescription description = getTaxonDescription(taxon, ref, false, true);
+ NamedAreaLevel level = makeNamedAreaLevel(state,classValue, next);
+
+ //status
+ PresenceAbsenceTerm status = null;
+ if (isNotBlank(statusValue)){
+ try {
+ status = state.getTransformer().getPresenceTermByKey(statusValue);
+ if (status == null){
+ UUID uuid = state.getTransformer().getPresenceTermUuid(statusValue);
+ if (uuid != null){
+ status = this.getPresenceAbsenceTerm(state, uuid, statusValue, statusValue, statusValue, false, null);
+ }
+ }
+ if (status == null){
+ //TODO
+ String message = "The presence/absence status '%s' could not be transformed to an CDM status";
+ fireWarningEvent(String.format(message, statusValue), next, 4);
+ }
+ } catch (UndefinedTransformerMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }else{
+ status = PresenceAbsenceTerm.PRESENT();
+ }
+ //frequency
+ if (isNotBlank(frequencyValue)){
+ String message = "The frequency attribute is currently not yet available in CDM";
+ fireWarningEvent(message, parentEvent, 6);
+ }
+
+ NamedArea higherArea = null;
+ List<NamedArea> areas = new ArrayList<NamedArea>();
+
+ String patSingleArea = "([^,\\(]{3,})";
+ String patSeparator = "(,|\\sand\\s)";
+ String hierarchiePattern = String.format("%s\\((%s(%s%s)*)\\)",patSingleArea, patSingleArea, patSeparator, patSingleArea);
+ Pattern patHierarchie = Pattern.compile(hierarchiePattern, Pattern.CASE_INSENSITIVE);
+ Matcher matcher = patHierarchie.matcher(label);
+ if (matcher.matches()){
+ String higherAreaStr = matcher.group(1).trim();
+ higherArea = makeArea(state, higherAreaStr, level);
+ String[] innerAreas = matcher.group(2).split(patSeparator);
+ for (String innerArea : innerAreas){
+ if (isNotBlank(innerArea)){
+ NamedArea singleArea = makeArea(state, innerArea.trim(), level);
+ areas.add(singleArea);
+ NamedArea partOf = singleArea.getPartOf();
+// if (partOf == null){
+// singleArea.setPartOf(higherArea);
+// }
+ }
+ }
+ }else{
+ NamedArea singleArea = makeArea(state, label, level);
+ areas.add(singleArea);
+ }
+
+ for (NamedArea area : areas){
+ //create distribution
+ Distribution distribution = Distribution.NewInstance(area,status);
+ description.addElement(distribution);
+ }
+ } else {
+ String message = "Empty distribution locality";
+ fireWarningEvent(message, next, 4);
+ }
+ return text;
+ } else if (isStartingElement(next, COORDINATES)) {
+ //TODO
+ handleNotYetImplementedElement(next);
+ } else if (isEndingElement(next, COORDINATES)) {
+ //TODO
+ popUnimplemented(next.asEndElement());
+ } else if (next.isCharacters()) {
+ text += next.asCharacters().getData();
+ } else {
+ handleUnexpectedElement(next);
+ }
+ }
+ throw new IllegalStateException("<DistributionLocality> has no closing tag");
+ }
+
+
+//********************************************** OLD *************************************
+
+// protected boolean testAdditionalElements(Element parentElement, List<String> excludeList){
+// boolean result = true;
+// List<Element> list = parentElement.getChildren();
+// for (Element element : list){
+// if (! excludeList.contains(element.getName())){
+// logger.warn("Unknown element (" + element.getName() + ") in parent element (" + parentElement.getName() + ")");
+// result = false;
+// }
+// }
+// return result;
+// }
+//
+//
+// protected <T extends IdentifiableEntity> T makeReferenceType(Element element, Class<? extends T> clazz, MapWrapper<? extends T> objectMap, ResultWrapper<Boolean> success){
+// T result = null;
+// String linkType = element.getAttributeValue("linkType");
+// String ref = element.getAttributeValue("ref");
+// if(ref == null && linkType == null){
+// result = getInstance(clazz);
+// if (result != null){
+// String title = element.getTextNormalize();
+// result.setTitleCache(title, true);
+// }
+// }else if (linkType == null || linkType.equals("local")){
+// //TODO
+// result = objectMap.get(ref);
+// if (result == null){
+// logger.warn("Object (ref = " + ref + ")could not be found in WrapperMap");
+// }
+// }else if(linkType.equals("external")){
+// logger.warn("External link types not yet implemented");
+// }else if(linkType.equals("other")){
+// logger.warn("Other link types not yet implemented");
+// }else{
+// logger.warn("Unknown link type or missing ref");
+// }
+// if (result == null){
+// success.setValue(false);
+// }
+// return result;
+// }
+//
+//
+// protected Reference makeAccordingTo(Element elAccordingTo, MapWrapper<Reference> referenceMap, ResultWrapper<Boolean> success){
+// Reference result = null;
+// if (elAccordingTo != null){
+// String childName = "AccordingToDetailed";
+// boolean obligatory = false;
+// Element elAccordingToDetailed = XmlHelp.getSingleChildElement(success, elAccordingTo, childName, elAccordingTo.getNamespace(), obligatory);
+//
+// childName = "Simple";
+// obligatory = true;
+// Element elSimple = XmlHelp.getSingleChildElement(success, elAccordingTo, childName, elAccordingTo.getNamespace(), obligatory);
+//
+// if (elAccordingToDetailed != null){
+// result = makeAccordingToDetailed(elAccordingToDetailed, referenceMap, success);
+// }else{
+// result = ReferenceFactory.newGeneric();
+// String title = elSimple.getTextNormalize();
+// result.setTitleCache(title, true);
+// }
+// }
+// return result;
+// }
+//
+//
+// private Reference makeAccordingToDetailed(Element elAccordingToDetailed, MapWrapper<Reference> referenceMap, ResultWrapper<Boolean> success){
+// Reference result = null;
+// Namespace tcsNamespace = elAccordingToDetailed.getNamespace();
+// if (elAccordingToDetailed != null){
+// //AuthorTeam
+// String childName = "AuthorTeam";
+// boolean obligatory = false;
+// Element elAuthorTeam = XmlHelp.getSingleChildElement(success, elAccordingToDetailed, childName, tcsNamespace, obligatory);
+// makeAccordingToAuthorTeam(elAuthorTeam, success);
+//
+// //PublishedIn
+// childName = "PublishedIn";
+// obligatory = false;
+// Element elPublishedIn = XmlHelp.getSingleChildElement(success, elAccordingToDetailed, childName, tcsNamespace, obligatory);
+// result = makeReferenceType(elPublishedIn, Reference.class, referenceMap, success);
+//
+// //MicroReference
+// childName = "MicroReference";
+// obligatory = false;
+// Element elMicroReference = XmlHelp.getSingleChildElement(success, elAccordingToDetailed, childName, tcsNamespace, obligatory);
+// String microReference = elMicroReference.getTextNormalize();
+// if (CdmUtils.Nz(microReference).equals("")){
+// //TODO
+// logger.warn("MicroReference not yet implemented for AccordingToDetailed");
+// }
+// }
+// return result;
+// }
+//
+// private Team makeAccordingToAuthorTeam(Element elAuthorTeam, ResultWrapper<Boolean> succes){
+// Team result = null;
+// if (elAuthorTeam != null){
+// //TODO
+// logger.warn("AuthorTeam not yet implemented for AccordingToDetailed");
+// }
+// return result;
+// }
+
+
+
+}