package eu.dnetlib.iis.wf.importer.concept; import java.io.IOException; import java.util.HashMap; import java.util.Stack; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import eu.dnetlib.iis.importer.schemas.Concept; import eu.dnetlib.iis.wf.importer.RecordReceiver; /** * Context profile SAX handler. Builds {@link Concept} objects based on XML profile with concepts hierarchy. * * @author mhorst * */ public class ConceptXmlHandler extends DefaultHandler { private static final String ELEM_CONCEPT = "concept"; private static final String ELEM_PARAM = "param"; private static final String ATTRIBUTE_ID = "id"; private static final String ATTRIBUTE_LABEL = "label"; private static final String ATTRIBUTE_NAME = "name"; private Stack<String> parentElementNames; private Stack<Concept.Builder> parentConcepts; private final RecordReceiver<Concept> receiver; private StringBuilder currentValue = new StringBuilder(); private String currentParamName; private Concept.Builder currentConceptBuilder; //-------------------- CONSTRUCTORS ------------------------- /** * @param receiver record receiver */ public ConceptXmlHandler(RecordReceiver<Concept> receiver) { super(); this.receiver = receiver; } //-------------------- LOGIC -------------------------------- @Override public void startDocument() throws SAXException { parentElementNames = new Stack<>(); parentConcepts = new Stack<>(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (isWithinElement(qName, ELEM_CONCEPT, null)) { if (currentConceptBuilder!=null) { parentConcepts.push(currentConceptBuilder); } currentConceptBuilder = Concept.newBuilder(); currentConceptBuilder.setId(attributes.getValue(ATTRIBUTE_ID)); currentConceptBuilder.setLabel(attributes.getValue(ATTRIBUTE_LABEL)); } else if (isWithinElement(qName, ELEM_PARAM, ELEM_CONCEPT)) { currentValue = new StringBuilder(); currentParamName = attributes.getValue(ATTRIBUTE_NAME); } parentElementNames.push(qName); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { this.parentElementNames.pop(); if (isWithinElement(qName, ELEM_CONCEPT, null)) { try { if (currentConceptBuilder==null) { currentConceptBuilder = parentConcepts.pop(); } receiver.receive(currentConceptBuilder.build()); currentConceptBuilder = null; } catch (IOException e) { throw new SAXException("Exception occurred when building concept object", e); } } else if (isWithinElement(qName, ELEM_PARAM, ELEM_CONCEPT)) { if (currentConceptBuilder==null) { currentConceptBuilder = parentConcepts.pop(); } if (!currentConceptBuilder.hasParams()) { currentConceptBuilder.setParams(new HashMap<>()); } currentConceptBuilder.getParams().put(currentParamName, currentValue.toString().trim()); } } @Override public void endDocument() throws SAXException { parentElementNames.clear(); parentElementNames = null; parentConcepts.clear(); parentConcepts = null; } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (currentValue != null) { currentValue.append(ch, start, length); } } //-------------------- PRIVATE -------------------------------- /** * Verifies position in XML tree by checking current element and optionally its parrent. * @param qName current element name * @param expectedElement expected element name * @param expectedParent expected parent element name */ private boolean isWithinElement(String qName, String expectedElement, String expectedParent) { return qName.equalsIgnoreCase(expectedElement) && (expectedParent == null || (!parentElementNames.isEmpty() && expectedParent.equalsIgnoreCase(parentElementNames.peek()))); } }