001/* 002 * JRecordBind, fixed-length file (un)marshaller 003 * Copyright 2019, Federico Fissore, and individual contributors. See 004 * AUTHORS.txt in the distribution for a full listing of individual 005 * contributors. 006 * 007 * This is free software; you can redistribute it and/or modify it 008 * under the terms of the GNU Lesser General Public License as 009 * published by the Free Software Foundation; either version 2.1 of 010 * the License, or (at your option) any later version. 011 * 012 * This software is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this software; if not, write to the Free 019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 021 */ 022 023package org.fissore.jrecordbind; 024 025import com.sun.xml.xsom.XSComplexType; 026import com.sun.xml.xsom.XSElementDecl; 027import com.sun.xml.xsom.XSSchema; 028import com.sun.xml.xsom.XSSchemaSet; 029import com.sun.xml.xsom.parser.JAXPParser; 030import com.sun.xml.xsom.parser.XSOMParser; 031import org.xml.sax.ErrorHandler; 032import org.xml.sax.InputSource; 033import org.xml.sax.SAXException; 034import org.xml.sax.SAXParseException; 035 036import javax.xml.parsers.SAXParserFactory; 037import java.io.File; 038import java.io.Reader; 039import java.net.MalformedURLException; 040import java.util.ArrayList; 041import java.util.List; 042import java.util.logging.Level; 043import java.util.logging.Logger; 044 045/** 046 * Reads the record definition and creates as many {@link RecordDefinition}s as needed 047 */ 048public class DefinitionLoader { 049 050 private final Logger log = Logger.getLogger(DefinitionLoader.class.getName()); 051 052 private final Evaluators evaluators; 053 private final RecordDefinition recordDefinition; 054 private boolean loaded; 055 056 public DefinitionLoader() { 057 this.recordDefinition = new RecordDefinition(); 058 this.evaluators = new Evaluators(); 059 this.loaded = false; 060 } 061 062 /** 063 * Parses the input .xsd and creates as many {@link RecordDefinition}s as needed 064 * 065 * @param file the XML Schema file with the file definition 066 * @return the loaded record definition 067 */ 068 public RecordDefinition load(File file) { 069 try { 070 return load(new InputSource(file.toURI().toURL().toExternalForm())); 071 } catch (MalformedURLException e) { 072 throw new RuntimeException(e); 073 } 074 } 075 076 /** 077 * Parses the input .xsd and creates as many {@link RecordDefinition}s as needed 078 * 079 * @param reader a reader that reads the XML Schema file with the file definition 080 * @return the loaded record definition 081 */ 082 public RecordDefinition load(Reader reader) { 083 return load(new InputSource(reader)); 084 } 085 086 /** 087 * Parses the input .xsd and creates as many {@link RecordDefinition}s as needed 088 * 089 * @param input an InputSource that reads the XML Schema file with the file definition 090 * @return the loaded record definition 091 */ 092 public RecordDefinition load(InputSource input) { 093 if (loaded) { 094 throw new IllegalStateException("Already loaded"); 095 } 096 097 log.log(Level.FINE, "Loading record definition"); 098 099 XSSchemaSet schemas = loadSchemas(input); 100 XSSchema mainSchema = findMainSchema(schemas); 101 XSElementDecl mainElement = mainSchema.getElementDecl("main"); 102 103 for (Evaluator<RecordDefinition, XSElementDecl> e : evaluators.mainElementEvaluators()) { 104 e.eval(recordDefinition, mainElement); 105 } 106 107 XSComplexType mainRecordType = mainElement.getType().asComplexType(); 108 for (Evaluator<RecordDefinition, XSComplexType> e : evaluators.typeEvaluators()) { 109 e.eval(recordDefinition, mainRecordType); 110 } 111 112 mainRecordType.getContentType().visit(new Visitor(evaluators, mainSchema, schemas, recordDefinition)); 113 114 this.loaded = true; 115 116 return recordDefinition; 117 } 118 119 private XSSchemaSet loadSchemas(InputSource input) { 120 XSOMParser parser = new XSOMParser(new JAXPParser(SAXParserFactory.newInstance())); 121 parser.setErrorHandler(new ErrorHandler() { 122 123 public void error(SAXParseException exception) { 124 throw new RuntimeException(exception); 125 } 126 127 public void fatalError(SAXParseException exception) { 128 throw new RuntimeException(exception); 129 } 130 131 public void warning(SAXParseException exception) { 132 throw new RuntimeException(exception); 133 } 134 135 }); 136 137 try { 138 parser.parse(input); 139 140 return parser.getResult(); 141 } catch (SAXException e) { 142 throw new RuntimeException(e); 143 } 144 } 145 146 private XSSchema findMainSchema(XSSchemaSet schemas) { 147 List<XSSchema> schemasWithMainElement = new ArrayList<>(); 148 for (XSSchema schema : schemas.getSchemas()) { 149 XSElementDecl main = schema.getElementDecl("main"); 150 if (main != null) { 151 schemasWithMainElement.add(schema); 152 } 153 } 154 155 if (schemasWithMainElement.isEmpty()) { 156 throw new IllegalArgumentException("No \"main\" element found"); 157 } 158 159 if (schemasWithMainElement.size() != 1) { 160 throw new IllegalArgumentException("Multiple \"main\" elements found in your schemas"); 161 } 162 163 return schemasWithMainElement.get(0); 164 } 165 166}