Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.regnosys.rosetta.translate.XMLParser Maven / Gradle / Ivy
package com.regnosys.rosetta.translate;
import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import com.regnosys.rosetta.common.hashing.ReferenceConfig;
import com.regnosys.rosetta.common.translation.Mapping;
import com.regnosys.rosetta.common.translation.MappingProcessor;
import com.regnosys.rosetta.common.translation.Path;
import com.regnosys.rosetta.common.translation.Path.PathElement;
import com.regnosys.rosetta.translate.ParserResult.Context;
import com.rosetta.model.lib.RosettaModelObject;
import com.rosetta.model.lib.RosettaModelObjectBuilder;
import com.rosetta.model.lib.path.RosettaPath;
public class XMLParser extends ParserParent {
private static final Logger LOGGER = LoggerFactory.getLogger(XMLParser.class);
public XMLParser(HandlerFactory factory, SynonymToEnumMapBuilder synonymToEnumMap, ReferenceConfig referenceConfig) {
super(factory, synonymToEnumMap, referenceConfig);
}
@Override
protected ParserResult parseTheDocument(Reader xmlReader, Schema schema, Class returnClass) {
char[] xmlChars = ParserHelper.toCharsfromReader(xmlReader);
List validationErrors = validateXML(xmlChars, schema);
Context context = new Context(synonymToEnumMap);
Map, HandlerSupplier> handlers = factory.getHandlerSuppliers(context.getMappingContext());
if (!handlers.containsKey(returnClass)) {
throw new IngestException("Do not have parser for " + returnClass.getSimpleName());
}
if (validationErrors.isEmpty()) {
try (Reader xml = new CharArrayReader(xmlChars)) {
ROMParseHandler> handler = (ROMParseHandler>) handlers.get(returnClass).apply(new Path(), new Path());
handler.setRosettaPath(new Path().addElement(new PathElement(returnClass.getSimpleName())));
XMLInputFactory inputFac = createXmlInputFactory();
XMLStreamReader xmlStreamReader = inputFac.createXMLStreamReader(xml);
parse(xmlStreamReader, handler, context);
RosettaModelObjectBuilder rosettaInstance = handler.getUnderlying();
LOGGER.debug("Finished parsing");
return new ParserResult(rosettaInstance, context);
} catch (XMLStreamException | IOException ioe) {
throw new IngestException("error reading input xml", ioe);
}
}
context.getXmlValidationErrors().addAll(validationErrors);
return new ParserResult(null, context);
}
protected static XMLInputFactory createXmlInputFactory() {
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, true);
return xmlInputFactory;
}
private List validateXML(char[] xmlChars, Schema schema) {
try (Reader xml = new CharArrayReader(xmlChars)) {
XmlErrorHandler xmlErrorHandler = new XmlErrorHandler();
Validator newValidator = schema.newValidator();
newValidator.setErrorHandler(xmlErrorHandler);
newValidator.validate(new StreamSource(xml));
LOGGER.debug("Finished validating xml against schema");
return xmlErrorHandler.getErrors();
} catch (SAXException | IOException e) {
throw new IngestException("Error validating xml against xsd", e);
}
}
private void parse(XMLStreamReader reader, ROMParseHandler> rootHandler, Context context) throws XMLStreamException {
Deque handlerStack = new ArrayDeque<>();
boolean end = false;
boolean rootElement = true;
HandlerStackItem currentHandlers =
new HandlerStackItem(Collections.singletonList(new HandlerWithPath(rootHandler)), new Path(Collections.emptyList()));
handlerStack.push(currentHandlers);
while (!end) {
reader.next();
List handlers = currentHandlers.handlers();
Path fullPath = currentHandlers.fullPath();
switch (reader.getEventType()) {
case XMLEvent.START_ELEMENT:
String qName = reader.getName().getLocalPart();
if (rootElement && rootHandler instanceof RootHandler) {
List allowedRootElements = ((RootHandler>) rootHandler).getAllowedRootElements();
if (!allowedRootElements.contains(qName)) {
context.getXmlValidationErrors().add(String.format("Invalid root XML element [%s], allowed elements %s", qName, allowedRootElements));
end = true;
break;
}
rootElement = false;
}
PathElement element = currentHandlers.addChild(qName, metas(reader));
Path newPath = fullPath.addElement(element);
//a new xml element - our current list of handlers either
//can't match the new element,
//matches the new element fully to give a new handler
//or partially matches and waits to see if the next element matches
//only the complex handlers are going to be able to accept this new element
List complexHandlers = handlers.stream()
.filter(HandlerWithPath::isComplexHandler)
.collect(Collectors.toList());
List newHandlers = complexHandlers.stream()
.flatMap(hwp -> hwp.matchingHandlers(element, fullPath).stream())
.collect(Collectors.toList());
complexHandlers.stream()
.flatMap(hwp -> hwp.matchingMappingProcessors(fullPath, context.getMappingContext()).stream())
.forEach(mapEntryProcessor ->
context.getMappingProcessors().put(mapEntryProcessor.getKey(), mapEntryProcessor.getValue()));
//If there are no handlers then this value cannot be mapped
if (newHandlers.isEmpty()) {
LOGGER.debug(newPath + " had no destination");
context.getMappingContext().getMappings().add(new Mapping(newPath, null, new Path(Collections.emptyList()), null, "had no destination", false, false, false));
}
handleAttributes(reader, newPath, newHandlers, context);
currentHandlers = new HandlerStackItem(newHandlers, newPath);
handlerStack.push(currentHandlers);
break;
case XMLEvent.CHARACTERS:
String stringVal = reader.getText().trim();
if (stringVal.length() > 0) {
context.getMappingContext().getMappings().addAll(HandlerWithPath.createMappings(handlers, stringVal, fullPath));
}
break;
case XMLEvent.END_ELEMENT:
//step out of the current handler context
handlerStack.pop();
if (handlerStack.isEmpty()) {
//should be no elements left to parse
end = true;
} else {
//remove the exited element from the stacks of the current handlers
handlerStack.peek().handlers().stream()
.filter(HandlerWithPath::isComplexHandler)
.forEach(HandlerWithPath::removeLastIfExists);
currentHandlers = handlerStack.peek();
}
break;
case XMLEvent.COMMENT:
// ignore comments
break;
case XMLEvent.START_DOCUMENT:
break;
case XMLEvent.END_DOCUMENT:
return;
default:
throw new AssertionError("Unhandled XMLEvent value: " + reader.getEventType());
}
}
}
private Map metas(XMLStreamReader reader) {
return IntStream.range(0, reader.getAttributeCount())
.mapToObj(Integer::valueOf)
.collect(Collectors.toMap(i -> reader.getAttributeName(i).getLocalPart(), i -> reader.getAttributeValue(i)));
}
private void handleAttributes(XMLStreamReader reader, Path path, List newHandlers, Context context) {
for (int i = 0; i < reader.getAttributeCount(); i++) {
if (reader.getAttributeName(i).getNamespaceURI().equals("http://www.w3.org/2001/XMLSchema-instance")) {
continue; // Ignore all schema related attributes
}
String attributeName = reader.getAttributeName(i).getLocalPart();
String attributeValue = reader.getAttributeValue(i);
if (attributeName.equals("fpmlVersion")) {
continue;//the FpML version is never mapped
}
Path aPath = path.addElement(new PathElement(attributeName));
List complexHandlers = newHandlers.stream()
.filter(HandlerWithPath::isComplexHandler)
.collect(Collectors.toList());
List attributeHandlers = new ArrayList<>();
Map attributeMappingProcessors = new HashMap<>();
for (HandlerWithPath complexHandler : complexHandlers) {
// Update path
complexHandler.addToStackIfNotNull(new PathElement(attributeName));
// Find all handlers for path
attributeHandlers.addAll(complexHandler.matchingHandlers(null, path));
// Find all mapping processors for path
complexHandler.matchingMappingProcessors(path, context.getMappingContext())
.forEach(e -> attributeMappingProcessors.put(e.getKey(), e.getValue()));
}
context.getMappingContext().getMappings().addAll(HandlerWithPath.createMappings(attributeHandlers, attributeValue, aPath));
if (!attributeMappingProcessors.isEmpty()) {
LOGGER.info(aPath + " has mapping processor");
}
attributeMappingProcessors.forEach((modelPath, mapper) -> {
context.getMappingProcessors().merge(modelPath, mapper, (existingMapper, newMapper) -> {
// if mapper already exists for model path, append the synonym path to the mapper's synonym paths.
existingMapper.getSynonymPaths().add(aPath);
return existingMapper;
});
});
newHandlers.stream()
.filter(HandlerWithPath::isComplexHandler)
.forEach(HandlerWithPath::removeLastIfExists);
}
}
static class XmlErrorHandler implements ErrorHandler {
List errors = new ArrayList<>();
@Override
public void warning(SAXParseException e) {
LOGGER.warn(getParseExceptionInfo(e));
}
@Override
public void error(SAXParseException e) {
String error = getParseExceptionInfo(e);
LOGGER.error(getParseExceptionInfo(e));
errors.add(error);
}
@Override
public void fatalError(SAXParseException e) throws SAXException {
String message = getParseExceptionInfo(e);
LOGGER.error("Fatal: " + message);
throw new SAXException(message);
}
private String getParseExceptionInfo(SAXParseException spe) {
return "URI=" + spe.getSystemId() + " Line=" + spe.getLineNumber() + ": " + spe.getMessage();
}
public boolean hasErrors() {
return !errors.isEmpty();
}
public List getErrors() {
return errors;
}
}
}