diff options
Diffstat (limited to 'ooxml/source/framework/SchemaParser')
89 files changed, 13926 insertions, 0 deletions
diff --git a/ooxml/source/framework/SchemaParser/.classpath b/ooxml/source/framework/SchemaParser/.classpath new file mode 100644 index 000000000000..fb565a588d8a --- /dev/null +++ b/ooxml/source/framework/SchemaParser/.classpath @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/ooxml/source/framework/SchemaParser/.project b/ooxml/source/framework/SchemaParser/.project new file mode 100644 index 000000000000..05958e177e81 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>SchemaParser</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/ooxml/source/framework/SchemaParser/.settings/org.eclipse.jdt.core.prefs b/ooxml/source/framework/SchemaParser/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000000..7341ab1683c4 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/SchemaReader.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/SchemaReader.java new file mode 100644 index 000000000000..bf15b39d71e2 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/SchemaReader.java @@ -0,0 +1,510 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Queue; +import java.util.Set; +import java.util.Vector; + +import javax.xml.stream.XMLStreamException; + +import org.apache.openoffice.ooxml.schema.automaton.FiniteAutomatonContainer; +import org.apache.openoffice.ooxml.schema.automaton.NonValidatingCreator; +import org.apache.openoffice.ooxml.schema.automaton.ValidatingCreator; +import org.apache.openoffice.ooxml.schema.generator.LogGenerator; +import org.apache.openoffice.ooxml.schema.generator.ParserTablesGenerator; +import org.apache.openoffice.ooxml.schema.generator.html.HtmlGenerator; +import org.apache.openoffice.ooxml.schema.model.schema.Schema; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; +import org.apache.openoffice.ooxml.schema.parser.SchemaParser; +import org.apache.openoffice.ooxml.schema.simple.SimpleTypeContainer; + +public class SchemaReader +{ + public static void main (final String ... aArgumentList) + { + if (aArgumentList.length != 1) + { + System.err.printf("usage: SchemaParser <driver-file>\n"); + System.err.printf(" driver file can contain these lines:\n"); + System.err.printf("# Comments\n"); + System.err.printf(" are ignored\n"); + System.err.printf("schema <mark> <file-name>\n"); + System.err.printf(" specifies a top-level schema file to read\n"); + System.err.printf("output-schema <file-name>\n"); + System.err.printf(" write schema information to file\n"); + System.err.printf("output-optimized-schema <file-name>\n"); + System.err.printf(" write information about optimized schema to file\n"); + System.exit(1); + } + + final SchemaReader aReader = new SchemaReader(new File(aArgumentList[0])); + aReader.Run(); + } + + + + + private SchemaReader (final File aDriverFile) + { + maSchemaBase = new SchemaBase(); + maTopLevelSchemas = new HashMap<>(); + maMainSchemaFiles = new Vector<>(); + maSchemaFiles = new HashSet<>(); + maWorkList = new LinkedList<>(); + maOutputOperations = new Vector<>(); + mnTotalLineCount = 0; + mnTotalByteCount = 0; + + ParseDriverFile(aDriverFile); + } + + + + + /** Read and parse the driver file that specifies which schema files to read + * and where the output should go. + */ + private void ParseDriverFile (final File aDriverFile) + { + if (aDriverFile == null || ! aDriverFile.exists() || ! aDriverFile.canRead()) + { + System.err.printf("can not read driver file\n"); + System.exit(1); + } + + try + { + final BufferedReader aIn = new BufferedReader(new FileReader(aDriverFile)); + while(true) + { + String sLine = aIn.readLine(); + if (sLine == null) + break; + // Lines starting with # are comment lines and are ignored. + if (sLine.matches("^\\s*#.*")) + continue; + // Lines containing only whitespace are also ignored. + else if (sLine.matches("^\\s*$")) + continue; + + // Handle line continuation. + while (sLine.endsWith("\\")) + sLine = sLine.substring(0, sLine.length()-1) + aIn.readLine(); + + final Vector<String> aParts = SplitLine(sLine); + switch (aParts.get(0)) + { + case "schema": + maMainSchemaFiles.add(new String[]{aParts.get(1), aParts.get(2)}); + break; + + case "output-schema": + maOutputOperations.add(new Runnable() + { + final File maFile = CreateCheckedOutputFile(aParts.get(1)); + @Override public void run() + { + WriteSchema(maFile); + } + }); + break; + + case "output-optimized-schema": + maOutputOperations.add(new Runnable() + { + final File maFile = CreateCheckedOutputFile(aParts.get(1)); + @Override public void run() + { + WriteOptimizedSchema(maFile); + } + }); + break; + + case "output-nonvalidating-parse-tables": + maOutputOperations.add(new Runnable() + { + final File aAutomatonLogFile = CreateCheckedOutputFile(aParts.get(1)); + final File aSimpleTypeLogFile = CreateCheckedOutputFile(aParts.get(2)); + final File aParseTableFile = CreateCheckedOutputFile(aParts.get(3)); + @Override public void run() + { + WriteNonValidatingParseTables( + aAutomatonLogFile, + aSimpleTypeLogFile, + aParseTableFile); + } + }); + break; + + case "output-validating-parse-tables": + maOutputOperations.add(new Runnable() + { + final File aAutomatonLogFile = CreateCheckedOutputFile(aParts.get(1)); + final File aSimpleTypeLogFile = CreateCheckedOutputFile(aParts.get(2)); + final File aParseTableFile = CreateCheckedOutputFile(aParts.get(3)); + @Override public void run() + { + WriteValidatingParseTables( + aAutomatonLogFile, + aSimpleTypeLogFile, + aParseTableFile); + } + }); + break; + + case "output-html-page": + maOutputOperations.add(new Runnable() + { + final File aHTMLPageFile = CreateCheckedOutputFile(aParts.get(1)); + @Override public void run() + { + WriteHTMLPage(aHTMLPageFile); + } + }); + break; + + default: + System.err.printf("unknown command '%s' in driver file", aParts.get(0)); + System.exit(1); + } + } + aIn.close(); + } + catch (final Exception aException) + { + aException.printStackTrace(); + } + } + + + + + private void Run () + { + try + { + ParseSchemaFiles(); + } + catch (final Exception aException) + { + aException.printStackTrace(); + } + + maOptimizedSchemaBase = maSchemaBase.GetOptimizedSchema(maTopLevelSchemas.values()); + for (final Entry<String, Schema> aEntry : maTopLevelSchemas.entrySet()) + aEntry.setValue(aEntry.getValue().GetOptimizedSchema(maOptimizedSchemaBase)); + + System.out.printf(" optimization left %d complex types and %d simple types\n", + maOptimizedSchemaBase.ComplexTypes.GetCount(), + maOptimizedSchemaBase.SimpleTypes.GetCount()); + + for (final Runnable aOperation : maOutputOperations) + { + aOperation.run(); + } + } + + + + + private void ParseSchemaFiles () + throws XMLStreamException + { + System.out.printf("parsing %d main schema files\n", maMainSchemaFiles.size()); + + for (final String[] aEntry : maMainSchemaFiles) + { + final String sMainSchemaShortname = aEntry[0]; + final String sMainSchemaFile = aEntry[1]; + final File aMainSchemaFile = new File(sMainSchemaFile); + if ( ! aMainSchemaFile.exists()) + { + System.err.printf(" schema file does not exist\n"); + System.exit(1); + } + if ( ! aMainSchemaFile.canRead()) + { + System.err.printf("can not read schema file\n"); + System.exit(1); + } + + final Schema aSchema = new Schema(sMainSchemaShortname, maSchemaBase); + ParseSchemaFile(sMainSchemaFile, aSchema); + maTopLevelSchemas.put(sMainSchemaShortname, aSchema); + } + + long nStartTime = System.currentTimeMillis(); + while ( ! maWorkList.isEmpty()) + { + ParseSchemaFile(maWorkList.poll(), null); + } + long nEndTime = System.currentTimeMillis(); + + System.out.printf("parsed %d schema files with a total of %d lines and %d bytes in %fs\n", + maSchemaFiles.size(), + mnTotalLineCount, + mnTotalByteCount, + (nEndTime-nStartTime)/1000.0); + System.out.printf(" found %d complex types and %d simple types\n", + maSchemaBase.ComplexTypes.GetCount(), + maSchemaBase.SimpleTypes.GetCount()); + + int nTopLevelElementCount = 0; + for (final Schema aSchema : maTopLevelSchemas.values()) + nTopLevelElementCount += aSchema.TopLevelElements.GetCount(); + System.out.printf(" the %d top level schemas have %d elements\n", + maTopLevelSchemas.size(), + nTopLevelElementCount); + } + + + + + private void ParseSchemaFile ( + final String sSchemaFilename, + final Schema aSchema) + throws XMLStreamException + { + System.out.printf("parsing %s\n", sSchemaFilename); + maSchemaFiles.add(sSchemaFilename); + + final SchemaParser aParser = new SchemaParser(new File(sSchemaFilename), aSchema, maSchemaBase); + aParser.Parse(); + + mnTotalLineCount += aParser.GetLineCount(); + mnTotalByteCount += aParser.GetByteCount(); + for (final File aFile : aParser.GetImportedSchemaFilenames()) + AddSchemaReference(aFile.getAbsolutePath()); + } + + + + + private void AddSchemaReference (final String sSchemaFilename) + { + if ( ! maSchemaFiles.contains(sSchemaFilename)) + { + if (sSchemaFilename == null) + throw new RuntimeException(); + + // We don't know yet the file name of the schema, so just store null to mark the schema name as 'known'. + maSchemaFiles.add(sSchemaFilename); + maWorkList.add(sSchemaFilename); + } + } + + + + + /** Split the given string at whitespace but not at whitespace inside double quotes. + * + */ + private Vector<String> SplitLine (final String sLine) + { + final Vector<String> aParts = new Vector<>(); + + boolean bIsInsideQuotes = false; + for (final String sPart : sLine.split("\"")) + { + if (bIsInsideQuotes) + aParts.add(sPart); + else + for (final String sInnerPart : sPart.split("\\s+")) + { + if (sInnerPart == null) + throw new RuntimeException(); + else if ( ! sInnerPart.isEmpty()) + aParts.add(sInnerPart); + } + + bIsInsideQuotes = ! bIsInsideQuotes; + } + + return aParts; + } + + + + + /** Create a File object for a given file name. + * Check that the file is writable, i.e. its directory exists and that if + * the file already exists it can be replaced. + * Throws a RuntimeException when a check fails. + */ + private File CreateCheckedOutputFile (final String sFilename) + { + final File aFile = new File(sFilename); + if ( ! aFile.getParentFile().exists()) + throw new RuntimeException("directory of "+sFilename+" does not exist: can not create file"); + if (aFile.exists() && ! aFile.canWrite()) + throw new RuntimeException("file "+sFilename+" already exists and can not be replaced"); + return aFile; + } + + + + + private void WriteSchema (final File aOutputFile) + { + LogGenerator.Write(aOutputFile, maSchemaBase, maTopLevelSchemas.values()); + } + + + + + private void WriteOptimizedSchema (final File aOutputFile) + { + LogGenerator.Write(aOutputFile, maOptimizedSchemaBase, maTopLevelSchemas.values()); + } + + + + + private void WriteNonValidatingParseTables ( + final File aAutomatonLogFile, + final File aSimpleTypeLogFile, + final File aParseTableFile) + { + long nStartTime = System.currentTimeMillis(); + final NonValidatingCreator aCreator = new NonValidatingCreator(maOptimizedSchemaBase, aAutomatonLogFile); + FiniteAutomatonContainer aAutomatons = aCreator.Create(maTopLevelSchemas.values()); + long nEndTime = System.currentTimeMillis(); + System.out.printf( + "created %d non-validating automatons with %d states and %d transitions in %fs\n", + aAutomatons.GetAutomatonCount(), + aAutomatons.GetStateCount(), + aAutomatons.GetTransitionCount(), + (nEndTime-nStartTime)/1000.0); + + nStartTime = System.currentTimeMillis(); + final SimpleTypeContainer aSimpleTypes = SimpleTypeContainer.Create( + maOptimizedSchemaBase, + aSimpleTypeLogFile); + nEndTime = System.currentTimeMillis(); + System.out.printf( + "created %d simple type descriptions in %fs\n", + aSimpleTypes.GetSimpleTypeCount(), + (nEndTime-nStartTime)/1000.0); + + new ParserTablesGenerator( + aAutomatons, + maOptimizedSchemaBase.Namespaces, + aSimpleTypes, + maOptimizedSchemaBase.AttributeValueToIdMap) + .Generate(aParseTableFile); + } + + + + + private void WriteValidatingParseTables ( + final File aAutomatonLogFile, + final File aSimpleTypeLogFile, + final File aParseTableFile) + { + long nStartTime = System.currentTimeMillis(); + final ValidatingCreator aCreator = new ValidatingCreator(maOptimizedSchemaBase, aAutomatonLogFile); + FiniteAutomatonContainer aAutomatons = aCreator.Create(); + long nEndTime = System.currentTimeMillis(); + System.out.printf( + "created %d validating stack automatons with %d states and %d transitions in %fs\n", + aAutomatons.GetAutomatonCount(), + aAutomatons.GetStateCount(), + aAutomatons.GetTransitionCount(), + (nEndTime-nStartTime)/1000.0); + + + nStartTime = System.currentTimeMillis(); + aAutomatons = aAutomatons.CreateDFAs(); + nEndTime = System.currentTimeMillis(); + System.out.printf( + "created %d deterministic automatons with %d states and %d transitions in %fs\n", + aAutomatons.GetAutomatonCount(), + aAutomatons.GetStateCount(), + aAutomatons.GetTransitionCount(), + (nEndTime-nStartTime)/1000.0); + + nStartTime = System.currentTimeMillis(); + aAutomatons = aAutomatons.MinimizeDFAs(); + nEndTime = System.currentTimeMillis(); + System.out.printf( + "minimized automaton in %fs, there are now %d states and %d transitions\n", + (nEndTime-nStartTime)/1000.0, + aAutomatons.GetStateCount(), + aAutomatons.GetTransitionCount()); + + nStartTime = System.currentTimeMillis(); + final SimpleTypeContainer aSimpleTypes = SimpleTypeContainer.Create( + maOptimizedSchemaBase, + aSimpleTypeLogFile); + nEndTime = System.currentTimeMillis(); + System.out.printf( + "created %d simple type descriptions in %fs\n", + aSimpleTypes.GetSimpleTypeCount(), + (nEndTime-nStartTime)/1000.0); + + new ParserTablesGenerator( + aAutomatons, + maOptimizedSchemaBase.Namespaces, + aSimpleTypes, + maOptimizedSchemaBase.AttributeValueToIdMap) + .Generate(aParseTableFile); + } + + + + + private void WriteHTMLPage ( + final File aHTMLPageFile) + { + long nStartTime = System.currentTimeMillis(); + + new HtmlGenerator(maOptimizedSchemaBase, maTopLevelSchemas, aHTMLPageFile).Generate(); + + long nEndTime = System.currentTimeMillis(); + System.out.printf( + "created HTML page in %fs\n", + (nEndTime-nStartTime)/1000.0); + } + + + + + private final SchemaBase maSchemaBase; + private SchemaBase maOptimizedSchemaBase; + private final Map<String,Schema> maTopLevelSchemas; + private final Vector<String[]> maMainSchemaFiles; + private final Queue<String> maWorkList; + private final Vector<Runnable> maOutputOperations; + private final Set<String> maSchemaFiles; + private int mnTotalLineCount; + private int mnTotalByteCount; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/Test.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/Test.java new file mode 100644 index 000000000000..5cfb3663aaca --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/Test.java @@ -0,0 +1,81 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema; + +import org.apache.openoffice.ooxml.schema.automaton.HopcroftMinimizer; +import org.apache.openoffice.ooxml.schema.automaton.State; +import org.apache.openoffice.ooxml.schema.automaton.StateContainer; +import org.apache.openoffice.ooxml.schema.automaton.StateContext; +import org.apache.openoffice.ooxml.schema.automaton.Transition; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** A simple test of the minimization algorithm for DFAs. + * + * May lead to the use of a testing framework in the future. + */ +public class Test +{ + public static void main (final String ... aArgumentList) + { + new Test("S", new String[]{"E"}, new String[][]{ + {"S", "A", "a"}, + {"A", "B", "b"}, + {"A", "C", "b"}, + {"B", "E", "c"}, + {"C", "E", "c"}, + }); + } + private Test ( + final String sStartState, + final String[] aAcceptingStates, + final String[][] aTransitions) + { + final StateContainer aOriginalStateContainer = new StateContainer(); + final StateContext aStates = new StateContext( + aOriginalStateContainer, + sStartState); + for (final String sAcceptingState : aAcceptingStates) + { + final State s = aStates.CreateState(sAcceptingState); + s.SetIsAccepting(); + } + for (final String[] aTransition : aTransitions) + { + final State start = aStates.GetOrCreateState( + new QualifiedName(aTransition[0]), + null); + final State end = aStates.GetOrCreateState( + new QualifiedName(aTransition[1]), + null); + final QualifiedName element = new QualifiedName(aTransition[2]); + final String type = "T_"+aTransition[2]; + + start.AddTransition(new Transition(start, end, element, type)); + } + HopcroftMinimizer.MinimizeDFA ( + new StateContainer(), + aStates, + null, + null, + System.out); + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/CreatorBase.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/CreatorBase.java new file mode 100644 index 000000000000..7f93ece2d00c --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/CreatorBase.java @@ -0,0 +1,124 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.iterator.AttributeIterator; +import org.apache.openoffice.ooxml.schema.iterator.DereferencingNodeIterator; +import org.apache.openoffice.ooxml.schema.misc.Log; +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +/** Base class of the creator classes for DFAs and NFAs. + */ +public class CreatorBase +{ + CreatorBase ( + final SchemaBase aSchemaBase, + final File aLogFile) + { + maSchemaBase = aSchemaBase; + maStateContainer = new StateContainer(); + maLog = new Log(aLogFile); + msLogIndentation = ""; + maElementSimpleTypes = new HashSet<>(); + } + + + + + /** Create a very simple automaton: + * a) Its start state is also the accepting state. + * b) It does not allow any transitions. + * c) Its text items have the given simple type. + */ + protected FiniteAutomaton CreateForSimpleType (final INode aSimpleType) + { + final StateContext aStateContext = new StateContext( + maStateContainer, + aSimpleType.GetName().GetStateName()); + aStateContext.GetStartState().SetIsAccepting(); + aStateContext.GetStartState().SetTextType(aSimpleType); + return new FiniteAutomaton( + aStateContext, + new Vector<Attribute>(), + aSimpleType.GetLocation()); + } + + + + + protected Vector<Attribute> CollectAttributes (final INode aRoot) + { + final Vector<Attribute> aAttributes = new Vector<>(); + for (final INode aNode : new DereferencingNodeIterator(aRoot, maSchemaBase, true)) + for (final Attribute aAttribute : new AttributeIterator(aNode, maSchemaBase)) + aAttributes.add(aAttribute); + return aAttributes; + } + + + + + protected void AddSkipTransition ( + final State aState, + final SkipData aSkipData) + { + aState.AddSkipData(aSkipData); + + if (maLog != null) + { + maLog.printf("%sskip state %s\n", + msLogIndentation, + aState.GetFullname()); + } + } + + + + + protected void ProcessAttributes (final INode aNode) + { + for (final Attribute aAttribute : new AttributeIterator(aNode, maSchemaBase)) + { + maLog.printf("%sattribute %s\n", + msLogIndentation, + aAttribute.GetName().GetDisplayName()); + maAttributes.add(aAttribute); + } + } + + + + + protected final SchemaBase maSchemaBase; + protected final StateContainer maStateContainer; + protected final Log maLog; + protected String msLogIndentation; + protected Vector<Attribute> maAttributes; + protected final Set<INode> maElementSimpleTypes; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/DFACreator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/DFACreator.java new file mode 100644 index 000000000000..c6684acfbb53 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/DFACreator.java @@ -0,0 +1,280 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Queue; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Convert an NFA into a DFA via the powerset construction (also called subset + * construction). + */ +public class DFACreator +{ + /** For a given non-deterministic finite automaton create an equivalent + * deterministic finite automaton. + */ + public static FiniteAutomaton CreateDFAforNFA ( + final StateContainer aDFAStateContainer, + final StateContext aNFAStateContext, + final Vector<Attribute> aAttributes, + final QualifiedName aTypeName, + final Location aLocation) + { + final DFACreator aCreator = new DFACreator(aDFAStateContainer, aNFAStateContext, aTypeName); + aCreator.CreateDFAforNFA(); + return new FiniteAutomaton( + aCreator.maDFAStateContext, + aAttributes, + aLocation); + } + + + + + private DFACreator ( + final StateContainer aDFAStateContainer, + final StateContext aNFAStateContext, + final QualifiedName aTypeName) + { + maNFAStateContext = aNFAStateContext; + + // Create the set of state sets where each element corresponds to a + // state in the DFA. + maNFASetToDFAStateMap = new TreeMap<>(); + maDFAStateContext = new StateContext( + aDFAStateContainer, + aTypeName == null + ? "<TOP-LEVEL>" + : aTypeName.GetStateName()); + + maDFATransitions = new HashSet<>(); + maAcceptingDFAStates = new Vector<>(); + } + + + + + private void CreateDFAforNFA () + { + final State aNFAStartState = maNFAStateContext.GetStartState(); + + // Initialize the creation process by adding the epsilon closure of the + // original start state to the work list. + final StateSet aStartSet = GetEpsilonClosure(new StateSet(aNFAStartState)); + maNFASetToDFAStateMap.put(aStartSet, maDFAStateContext.GetStartState()); + + PropagateStateFlags(aStartSet, maDFAStateContext.GetStartState()); + + final Queue<StateSet> aWorklist = new LinkedList<>(); + aWorklist.add(aStartSet); + + while ( ! aWorklist.isEmpty()) + { + final Collection<StateSet> aAdditionalWorkList = ProcessTransitionFront( + aWorklist.poll()); + + aWorklist.addAll(aAdditionalWorkList); + } + } + + + + + private Collection<StateSet> ProcessTransitionFront ( + final StateSet aSet) + { + final Set<StateSet> aLocalWorklist = new TreeSet<>(); + + // Find all regular transitions that start from any state in the set. + final Map<String,Vector<Transition>> aTransitions = GetTransitionFront(aSet); + + // Create new state sets for states that are reachable via the same element and + // the following epsilon transitions. + for (final Entry<String,Vector<Transition>> aEntry : aTransitions.entrySet()) + { + // Create new state sets for both the end state of the transition. + final StateSet aEpsilonClosure = GetEpsilonClosure(GetEndStateSet(aEntry.getValue())); + + // When these are new state sets then add them to the worklist + // and the set of sets. + State aDFAState = maNFASetToDFAStateMap.get(aEpsilonClosure); + if (aDFAState == null) + { + aLocalWorklist.add(aEpsilonClosure); + aDFAState = aEpsilonClosure.CreateStateForStateSet(maDFAStateContext); + PropagateStateFlags(aEpsilonClosure, aDFAState); + maNFASetToDFAStateMap.put(aEpsilonClosure, aDFAState); + if (aDFAState.IsAccepting()) + maAcceptingDFAStates.add(aDFAState); + } + + final State aStartState = maNFASetToDFAStateMap.get(aSet); + final QualifiedName aElementName = GetElementName(aEntry.getValue()); + final String sElementTypeName = GetElementTypeName(aEntry.getValue()); + assert(aElementName != null); + final Transition aTransition = new Transition( + aStartState, + aDFAState, + aElementName, + sElementTypeName); + aStartState.AddTransition(aTransition); + maDFATransitions.add(aTransition); + } + + return aLocalWorklist; + } + + + + + private QualifiedName GetElementName (final Vector<Transition> aTransitions) + { + for (final Transition aTransition : aTransitions) + return aTransition.GetElementName(); + return null; + } + + + + + private String GetElementTypeName (final Vector<Transition> aTransitions) + { + for (final Transition aTransition : aTransitions) + return aTransition.GetElementTypeName(); + return null; + } + + + + + /** Return the epsilon closure of the given set of states. + * The result is the set of all states that are reachable via zero, one or + * more epsilon transitions from at least one state in the given set of + * states. + */ + private StateSet GetEpsilonClosure ( final StateSet aSet) + { + final StateSet aClosure = new StateSet(aSet); + + final Queue<State> aWorkList = new LinkedList<>(); + for (final State aState : aSet.GetStates()) + aWorkList.add(aState); + + while( ! aWorkList.isEmpty()) + { + final State aState = aWorkList.poll(); + for (final EpsilonTransition aTransition : aState.GetEpsilonTransitions()) + { + final State aEndState = aTransition.GetEndState(); + if ( ! aClosure.ContainsState(aEndState)) + { + aClosure.AddState(aEndState); + aWorkList.add(aEndState); + } + } + } + + return aClosure; + } + + + + + /** Return the list of regular transitions (i.e. not epsilon transitions) + * that start from any of the states in the given set. + * The returned map is a partition of the transitions according to their + * triggering XML element. + */ + private Map<String, Vector<Transition>> GetTransitionFront (final StateSet aSet) + { + final Map<String, Vector<Transition>> aTransitions = new HashMap<>(); + + for (final State aState : aSet.GetStates()) + for (final Transition aTransition : aState.GetTransitions()) + { + final String sElementName; + final QualifiedName aElementName = aTransition.GetElementName(); + if (aElementName != null) + sElementName = aElementName.GetDisplayName(); + else + sElementName = null; // For skip transitions. + + Vector<Transition> aElementTransitions = aTransitions.get(sElementName); + if (aElementTransitions == null) + { + aElementTransitions = new Vector<>(); + aTransitions.put(sElementName, aElementTransitions); + } + aElementTransitions.add(aTransition); + } + return aTransitions; + } + + + + + /** Return a state set that contains all end states of all the given transitions. + */ + private StateSet GetEndStateSet (final Iterable<Transition> aTransitions) + { + final StateSet aStateSet = new StateSet(); + for (final Transition aTransition : aTransitions) + aStateSet.AddState(aTransition.GetEndState()); + return aStateSet; + } + + + + + /** Propagate accepting state flag and skip data. + */ + private void PropagateStateFlags ( + final StateSet aNFAStateSet, + final State aDFAState) + { + for (final State aNFAState : aNFAStateSet.GetStates()) + aDFAState.CopyFrom(aNFAState); + } + + + + + private final StateContext maNFAStateContext; + + private final Map<StateSet,State> maNFASetToDFAStateMap; + private final StateContext maDFAStateContext; + private final Set<Transition> maDFATransitions; + private final Vector<State> maAcceptingDFAStates; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/EpsilonTransition.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/EpsilonTransition.java new file mode 100644 index 000000000000..75dca97ed6eb --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/EpsilonTransition.java @@ -0,0 +1,70 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +/** Transition from one state to another that does not consume an input token. + * + * Use in the process of creating a validating parser. + */ +public class EpsilonTransition +{ + EpsilonTransition ( + final State aStartState, + final State aEndState) + { + maStartState = aStartState; + maEndState = aEndState; + } + + + + + public State GetStartState () + { + return maStartState; + } + + + + + public State GetEndState () + { + return maEndState; + } + + + + + @Override + public String toString () + { + return String.format("%s -> %s", + maStartState.GetFullname(), + maEndState.GetFullname()); + } + + + + + private final State maStartState; + private final State maEndState; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/FiniteAutomaton.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/FiniteAutomaton.java new file mode 100644 index 000000000000..16bf2f737c8d --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/FiniteAutomaton.java @@ -0,0 +1,169 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + + +/** Represents a DFA (deterministic FA) or a NFA (non-deterministic FA). + * There is one automaton for each complex type and one for the top level elements. + * Transitions correspond to 'element' elements in the schema or a start tag in + * the input file. During parsing the current automaton is pushed on a stack + * and the automaton that represents the complex type associated with the + * starting element is made the current automaton. An end tag pops an automaton + * from the stack and replaces the current automaton with it. + */ +public class FiniteAutomaton +{ + FiniteAutomaton ( + final StateContext aContext, + final Vector<Attribute> aAttributes, + final Location aLocation) + { + maStateContext = aContext; + maAttributes = aAttributes!=null + ? aAttributes + : new Vector<Attribute>(); + maLocation = aLocation; + } + + + + + public int GetStateCount () + { + return maStateContext.GetStateCount(); + } + + + + + public Iterable<State> GetStates() + { + return maStateContext.GetStates(); + } + + + + + public Iterable<State> GetStatesSorted () + { + return maStateContext.GetStatesSorted(); + } + + + + + public State GetStartState () + { + return maStateContext.GetStartState(); + } + + + + + public Iterable<State> GetAcceptingStates () + { + return maStateContext.GetAcceptingStates(); + } + + + + + public FiniteAutomaton CreateDFA ( + final StateContainer aDFAContainer, + final QualifiedName aTypeName) + { + return DFACreator.CreateDFAforNFA( + aDFAContainer, + maStateContext, + maAttributes, + aTypeName, + maLocation); + } + + + + + public StateContext GetStateContext() + { + return maStateContext; + } + + + + + public Iterable<Transition> GetTransitions () + { + final Vector<Transition> aTransitions = new Vector<>(); + for (final State aState : maStateContext.GetStates()) + for (final Transition aTransition : aState.GetTransitions()) + aTransitions.add(aTransition); + return aTransitions; + } + + + + + public int GetTransitionCount() + { + int nTransitionCount = 0; + for (final State aState : maStateContext.GetStates()) + nTransitionCount += aState.GetTransitionCount(); + return nTransitionCount; + } + + + + + public String GetTypeName () + { + return maStateContext.GetStartState().GetFullname(); + } + + + + + public Location GetLocation () + { + return maLocation; + } + + + + + public Vector<Attribute> GetAttributes () + { + return maAttributes; + } + + + + + private final StateContext maStateContext; + private final Vector<Attribute> maAttributes; + private final Location maLocation; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/FiniteAutomatonContainer.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/FiniteAutomatonContainer.java new file mode 100644 index 000000000000..e76b7e8614e7 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/FiniteAutomatonContainer.java @@ -0,0 +1,166 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** As there is one FA for each complex type and one for the top level elements, + * this container represents the whole set of schemas. + */ +public class FiniteAutomatonContainer +{ + FiniteAutomatonContainer (final StateContainer aStateContainer) + { + maComplexTypeNameToAutomatonMap = new HashMap<>(); + } + + + + + public void AddAutomaton ( + final QualifiedName aElementName, + final FiniteAutomaton aAutomaton) + { + maComplexTypeNameToAutomatonMap.put(aElementName, aAutomaton); + } + + + + + public Iterable<FiniteAutomaton> GetAutomatons() + { + return maComplexTypeNameToAutomatonMap.values(); + } + + + + + public int GetAutomatonCount () + { + return maComplexTypeNameToAutomatonMap.size(); + } + + + + + public Iterable<State> GetStates() + { + final Vector<State> aStates = new Vector<>(); + for (final FiniteAutomaton aAutomaton : maComplexTypeNameToAutomatonMap.values()) + for (final State aState : aAutomaton.GetStates()) + aStates.add(aState); + return aStates; + } + + + + + public int GetStateCount() + { + int nStateCount = 0; + for (final FiniteAutomaton aAutomaton : maComplexTypeNameToAutomatonMap.values()) + nStateCount += aAutomaton.GetStateCount(); + return nStateCount; + } + + + + + public int GetTransitionCount () + { + int nTransitionCount = 0; + for (final FiniteAutomaton aAutomaton : maComplexTypeNameToAutomatonMap.values()) + nTransitionCount += aAutomaton.GetTransitionCount(); + return nTransitionCount; + } + + + + + public FiniteAutomatonContainer CreateDFAs () + { + final StateContainer aDFAStateContainer = new StateContainer(); + final FiniteAutomatonContainer aDFAs = new FiniteAutomatonContainer(aDFAStateContainer); + for (final Entry<QualifiedName, FiniteAutomaton> aEntry : maComplexTypeNameToAutomatonMap.entrySet()) + { + aDFAs.AddAutomaton( + aEntry.getKey(), + aEntry.getValue().CreateDFA( + aDFAStateContainer, + aEntry.getKey())); + } + return aDFAs; + } + + + + + public FiniteAutomatonContainer MinimizeDFAs () + { + PrintStream aLog = null; + try + { + aLog = new PrintStream(new FileOutputStream(new File("/tmp/minimization.log"))); + } + catch(Exception e) + { + e.printStackTrace(); + return null; + } + + final StateContainer aNewStateContainer = new StateContainer(); + final FiniteAutomatonContainer aDFAs = new FiniteAutomatonContainer(aNewStateContainer); + for (final Entry<QualifiedName, FiniteAutomaton> aEntry : maComplexTypeNameToAutomatonMap.entrySet()) + { + aDFAs.AddAutomaton( + aEntry.getKey(), + HopcroftMinimizer.MinimizeDFA( + aNewStateContainer, + aEntry.getValue().GetStateContext(), + aEntry.getValue().GetAttributes(), + aEntry.getValue().GetLocation(), + aLog)); + } + return aDFAs; + } + + + + + public FiniteAutomaton GetTopLevelAutomaton () + { + return maComplexTypeNameToAutomatonMap.get(null); + } + + + + + private final Map<QualifiedName, FiniteAutomaton> maComplexTypeNameToAutomatonMap; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/HopcroftMinimizer.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/HopcroftMinimizer.java new file mode 100644 index 000000000000..9177ccc707ae --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/HopcroftMinimizer.java @@ -0,0 +1,380 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import java.io.PrintStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Minimize an DFA with respect to its number of states. + * This is most important for the use of the 'all' element in the OOXML + * specification which leads to a lot of additional states and transitions. + */ +public class HopcroftMinimizer +{ + /** Create a DFA that is equivalent to a given DFA but has the minimal + * number of states. + */ + public static FiniteAutomaton MinimizeDFA ( + final StateContainer aNewStateContainer, + final StateContext aOriginalStates, + final Vector<Attribute> aAttributes, + final Location aLocation, + final PrintStream aLog) + { + if (aLog != null) + { + aLog.printf("minimizing %d states and %d transitions\n", + aOriginalStates.GetStateCount(), + aOriginalStates.GetTransitionCount()); + DisplayStates(aOriginalStates, aLog); + } + + TreeSet<StateSet> aT = new TreeSet<>(); + TreeSet<StateSet> aP = new TreeSet<>(); + Map<State,StateSet> aTMap = new HashMap<>(); + Map<State,StateSet> aPMap = new HashMap<>(); + InitializeMap(aT, aTMap, aOriginalStates.GetStates()); + + // Split partitions until there is nothing else to do. + while ( ! AreSetsOfStateSetsEqual(aP, aT)) + { + if (aLog != null) + aLog.printf("T has %d members\n", aT.size()); + + aP = aT; + aPMap = aTMap; + aT = new TreeSet<>(); + aTMap = new HashMap<>(); + + for (final StateSet aSet : aP) + { + final Iterable<StateSet> aParts = Split(aSet, aP, aPMap); + if (aParts == null) + { + // No split necessary. + assert( ! aSet.IsEmpty()); + aT.add(aSet); + for (final State aState : aSet.GetStates()) + aTMap.put(aState, aSet); + } + else + { + for (final StateSet aPart : aParts) + { + assert( ! aPart.IsEmpty()); + aT.add(aPart); + + for (final State aState : aPart.GetStates()) + aTMap.put(aState, aPart); + } + } + } + } + + // Create new states. + final StateContext aMinimizedStates = CreateNewStates( + aP, + aPMap, + aNewStateContainer, + aOriginalStates); + + if (aLog != null) + { + aLog.printf("to %d states and %d transitions\n", + aMinimizedStates.GetStateCount(), + aMinimizedStates.GetTransitionCount()); + DisplayStates(aMinimizedStates, aLog); + for (final StateSet aSet : aT) + aLog.printf(" %s\n", aSet.toString()); + } + + // Create and return the new minimized automaton. + return new FiniteAutomaton( + aMinimizedStates, + aAttributes, + aLocation); + } + + + + + /** We start with two sets. One contains all start states (in our case + * just one), the other contains all other states. + */ + private static void InitializeMap ( + final Set<StateSet> aSet, + final Map<State,StateSet> aMap, + final Iterable<State> aStates) + { + final StateSet aAcceptingStates = new StateSet(); + final StateSet aNonAcceptingStates = new StateSet(); + for (final State aState : aStates) + { + if (aState.IsAccepting()) + { + aAcceptingStates.AddState(aState); + aMap.put(aState, aAcceptingStates); + } + else + { + aNonAcceptingStates.AddState(aState); + aMap.put(aState, aNonAcceptingStates); + } + } + if (aAcceptingStates.IsEmpty()) + throw new RuntimeException("there should be at least one accepting state"); + aSet.add(aAcceptingStates); + if ( ! aNonAcceptingStates.IsEmpty()) + aSet.add(aNonAcceptingStates); + } + + + + + private static Iterable<StateSet> Split ( + final StateSet aSet, + final Set<StateSet> aT, + final Map<State,StateSet> aTMap) + { + if (aSet.GetStateCount() == 1) + return null; + + final Set<QualifiedName> aElements = CollectElementNames(aSet); + for (final QualifiedName aElementName : aElements) + { + final Collection<StateSet> aPartitions = Split(aSet, aT, aTMap, aElementName); + if (aPartitions == null) + continue; + if (aPartitions.size() > 1) + return aPartitions; + } + return null; + } + + + + + /** Create a partition of the given set of states according to their + * transitions. + * All states whose transitions point to the same state set go in the same + * partition. + */ + private static Collection<StateSet> Split ( + final StateSet aSet, + final Set<StateSet> aT, + final Map<State,StateSet> aTMap, + final QualifiedName aElementName) + { + // Set up a forward map that does two steps: + // from s via transition regarding aElementName to s' + // from s' to a state set under aTMap(s). + final Map<State,StateSet> aForwardMap = new HashMap<>(); + for (final State aState : aSet.GetStates()) + { + final Transition aTransition = GetTransition(aState, aElementName); + if (aTransition == null) + aForwardMap.put(aState, null); + else + aForwardMap.put(aState, aTMap.get(aTransition.GetEndState())); + } + + // Create the partion of aSet according to aForwardMap. All states that map + // to the same element go into the same state set. + if (aForwardMap.size() == 1) + { + // No split necessary. + return null; + } + else + { + // Set up a reverse map that maps that maps the values in aForwardMap to + // new state sets whose contents are the keys in aForwardMap. + final Map<StateSet,StateSet> aReverseMap = new HashMap<>(); + for (final Entry<State,StateSet> aEntry : aForwardMap.entrySet()) + { + StateSet aPartitionSet = aReverseMap.get(aEntry.getValue()); + if (aPartitionSet == null) + { + aPartitionSet = new StateSet(); + aReverseMap.put(aEntry.getValue(), aPartitionSet); + } + aPartitionSet.AddState(aEntry.getKey()); + } + return aReverseMap.values(); + } + } + + + + + private static Transition GetTransition ( + final State aState, + final QualifiedName aElementName) + { + Transition aTransition = null; + for (final Transition aCandidate : aState.GetTransitions()) + if (aCandidate.GetElementName().compareTo(aElementName) == 0) + { + assert(aTransition==null); + aTransition = aCandidate; + // break; + } + return aTransition; + } + + + + + private static Set<QualifiedName> CollectElementNames (final StateSet aSet) + { + final Set<QualifiedName> aNames = new TreeSet<>(); + for (final State aState : aSet.GetStates()) + for (final Transition aTransition : aState.GetTransitions()) + aNames.add(aTransition.GetElementName()); + + return aNames; + } + + + + + private static boolean AreSetsOfStateSetsEqual ( + final TreeSet<StateSet> aSetOfSetsA, + final TreeSet<StateSet> aSetOfSetsB) + { + if (aSetOfSetsA.size() != aSetOfSetsB.size()) + return false; + else + { + final Iterator<StateSet> aSetIteratorA = aSetOfSetsA.iterator(); + final Iterator<StateSet> aSetIteratorB = aSetOfSetsB.iterator(); + while (aSetIteratorA.hasNext() && aSetIteratorB.hasNext()) + { + if (aSetIteratorA.next().compareTo(aSetIteratorB.next()) != 0) + return false; + } + return true; + } + } + + + + + private static StateContext CreateNewStates ( + final TreeSet<StateSet> aP, + final Map<State,StateSet> aPMap, + final StateContainer aNewStateContainer, + final StateContext aOriginalStates) + { + final StateContext aMinimizedStates = new StateContext( + aNewStateContainer, + aOriginalStates.GetStartState().GetFullname()); + + // Create the new states. + final Map<State,State> aOldStateToNewStateMap = new TreeMap<>(); + for (final StateSet aSet : aP) + { + State aNewState = null; + for (final State aOldState : aSet.GetStates()) + { + if (aNewState == null) + aNewState = aOldState.Clone(aMinimizedStates); + aOldStateToNewStateMap.put(aOldState, aNewState); + } + } + + // Create the new transitions. + for (final StateSet aSet : aP) + { + final State aOldStartState = aSet.GetStates().iterator().next(); + final State aNewStartState = aOldStateToNewStateMap.get(aOldStartState); + + for (final Transition aTransition : aOldStartState.GetTransitions()) + { + final State aOldEndState = aTransition.GetEndState(); + final State aNewEndState = aOldStateToNewStateMap.get(aOldEndState); + + // Check if the transition already exists. + if (HasTransition(aNewStartState, aTransition.GetElementName())) + continue; + + aNewStartState.AddTransition( + new Transition( + aNewStartState, + aNewEndState, + aTransition.GetElementName(), + aTransition.GetElementTypeName())); + } + } + + // Transfer skip data and accepting flags. + for (final State aOldState : aOriginalStates.GetStates()) + { + final State aNewState = aOldStateToNewStateMap.get(aOldState); + aNewState.CopyFrom(aOldState); + } + return aMinimizedStates; + } + + + + + private static boolean HasTransition ( + final State aState, + final QualifiedName aElementName) + { + for (final Transition aTransition : aState.GetTransitions()) + if (aTransition.GetElementName().compareTo(aElementName) == 0) + return true; + return false; + } + + + + + private static void DisplayStates ( + final StateContext aStates, + final PrintStream aLog) + { + for (final State aState : aStates.GetStates()) + { + aLog.printf(" %s %s\n", aState.GetFullname(), + aState.IsAccepting() ? "is accepting" : ""); + for (final Transition aTransition : aState.GetTransitions()) + aLog.printf(" -> %s via %s\n", + aTransition.GetEndState().GetFullname(), + aTransition.GetElementName().GetStateName()); + } + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/NonValidatingCreator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/NonValidatingCreator.java new file mode 100644 index 000000000000..f96e3237a431 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/NonValidatingCreator.java @@ -0,0 +1,212 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import java.io.File; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.iterator.DereferencingNodeIterator; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.complex.Any; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexType; +import org.apache.openoffice.ooxml.schema.model.complex.Element; +import org.apache.openoffice.ooxml.schema.model.schema.Schema; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +/** Create a set of stack automatons for a given set of schemas. + * Creates one automaton for each complex type and one for the top level elements. + * + * Input files but are not validated to conform to the schemas. + */ +public class NonValidatingCreator + extends CreatorBase +{ + public NonValidatingCreator ( + final SchemaBase aSchemaBase, + final File aLogFile) + { + super(aSchemaBase, aLogFile); + } + + + + + public FiniteAutomatonContainer Create ( + final Iterable<Schema> aTopLevelSchemas) + { + final FiniteAutomatonContainer aAutomatons = new FiniteAutomatonContainer(maStateContainer); + + // Create a single automaton for all top level elements. + aAutomatons.AddAutomaton( + null, + CreateForTopLevelElements(aTopLevelSchemas)); + + // Create one automaton for each complex type. + for (final ComplexType aComplexType : maSchemaBase.ComplexTypes.GetSorted()) + aAutomatons.AddAutomaton( + aComplexType.GetName(), + CreateForComplexType(aComplexType)); + + // Create one automaton for each simple type that is referenced by an element. + for (final INode aSimpleType : maElementSimpleTypes) + aAutomatons.AddAutomaton( + aSimpleType.GetName(), + CreateForSimpleType(aSimpleType)); + + maLog.Close(); + + return aAutomatons; + } + + + + + private FiniteAutomaton CreateForTopLevelElements ( + final Iterable<Schema> aTopLevelSchemas) + { + maLog.AddComment("top level elements"); + maLog.StartBlock(); + final String sTypeName = "<top-level>"; + final StateContext aStateContext = new StateContext( + maStateContainer, + sTypeName); + final State aStartState = aStateContext.GetStartState(); + final State aEndState = aStateContext.CreateEndState(); + + // top level elements + for (final Schema aSchema : aTopLevelSchemas) + { + maLog.AddComment("schema %s", aSchema.GetShortName()); + maLog.StartBlock(); + for (final Element aElement : aSchema.TopLevelElements.GetSorted()) + { + maLog.AddComment("Element: on '%s' go from %s to %s via %s", + aElement.GetElementName().GetDisplayName(), + aStartState.GetFullname(), + aEndState.GetFullname(), + aElement.GetTypeName().GetStateName()); + + aStateContext.GetStartState().AddTransition( + new Transition( + aStartState, + aEndState, + aElement.GetElementName(), + aElement.GetTypeName().GetStateName())); + } + maLog.EndBlock(); + } + maLog.EndBlock(); + + return new FiniteAutomaton(aStateContext, null, null); + } + + + + + private FiniteAutomaton CreateForComplexType (final ComplexType aComplexType) + { + maLog.printf("\n"); + maLog.AddComment ("Complex Type %s defined in %s.", + aComplexType.GetName().GetDisplayName(), + aComplexType.GetLocation()); + maLog.StartBlock(); + + final StateContext aStateContext = new StateContext( + maStateContainer, + aComplexType.GetName().GetStateName()); + + for (final Element aElement : CollectElements(aComplexType)) + { + maLog.AddComment("Element: on '%s' go from %s to %s via %s", + aElement.GetElementName().GetDisplayName(), + aStateContext.GetStartState().GetFullname(), + aStateContext.GetStartState().GetFullname(), + aElement.GetTypeName().GetStateName()); + + aStateContext.GetStartState().AddTransition( + new Transition( + aStateContext.GetStartState(), + aStateContext.GetStartState(), + aElement.GetElementName(), + aElement.GetTypeName().GetStateName())); + + // For elements whose type is a simple type we have to remember that + // simple type for later (and then create an NFA for it.) + final INode aSimpleType = maSchemaBase.GetSimpleTypeForName( + aElement.GetTypeName()); + if (aSimpleType != null) + maElementSimpleTypes.add(aSimpleType); + } + + for (final Any aAny : CollectAnys(aComplexType)) + { + AddSkipTransition( + aStateContext.GetStartState(), + new SkipData( + aAny.GetProcessContentsFlag(), + aAny.GetNamespaces())); + } + + // Collect all attributes. + maAttributes = new Vector<>(); + for (final INode aNode : new DereferencingNodeIterator(aComplexType, maSchemaBase, true)) + ProcessAttributes(aNode); + + aStateContext.GetStartState().SetIsAccepting(); + + maLog.EndBlock(); + + return new FiniteAutomaton(aStateContext, maAttributes, aComplexType.GetLocation()); + } + + + + + /** Collect all elements inside the type tree that is rooted in the given + * complex type. + */ + private Vector<Element> CollectElements (final ComplexType aType) + { + final Vector<Element> aElements = new Vector<>(); + for (final INode aNode : new DereferencingNodeIterator(aType, maSchemaBase, false)) + { + if (aNode.GetNodeType() == NodeType.Element) + aElements.add((Element)aNode); + } + return aElements; + } + + + + + private Vector<Any> CollectAnys (final ComplexType aType) + { + final Vector<Any> aAnys = new Vector<>(); + for (final INode aNode : new DereferencingNodeIterator(aType, maSchemaBase, false)) + { + if (aNode.GetNodeType() == NodeType.Any) + aAnys.add((Any)aNode); + } + return aAnys; + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/SkipData.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/SkipData.java new file mode 100644 index 000000000000..f5c051caad4d --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/SkipData.java @@ -0,0 +1,54 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import org.apache.openoffice.ooxml.schema.model.complex.Any; + +/** Description of optional content that can be skipped when not supported. + * Corresponds to the 'any' schema element. + */ +public class SkipData +{ + public SkipData ( + final Any.ProcessContents aProcessContents, + final String[] aNamespaces) + { + maProcessContents = aProcessContents; + maNamespaces = aNamespaces; + } + + + + + public SkipData Clone (final State aState) + { + return new SkipData( + maProcessContents, + maNamespaces); + } + + + + + final Any.ProcessContents maProcessContents; + final String[] maNamespaces; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/State.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/State.java new file mode 100644 index 000000000000..7d9982c0e5c2 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/State.java @@ -0,0 +1,263 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Each complex type is represented by a State object (primary state). + * For a validating parser additional states are created for sequences, choices, etc. (secondary states). + * Secondary states have the same basename as primary states and suffixes to make their names unique. + * Full names of states contain both the basename and the suffix. + */ +public class State + implements Comparable<State> +{ + /** Create a new state from a basename and an optional suffix. + * + * Don't call this constructor directly. Use methods in StateContext instead. + * They ensure that states are unique per context. + */ + State ( + final QualifiedName aBasename, + final String sSuffix) + { + maBasename = aBasename; + msSuffix = sSuffix; + msFullname = GetStateName(aBasename, msSuffix); + maTransitions = new Vector<>(); + maEpsilonTransitions = new Vector<>(); + maSkipData = new Vector<>(); + mbIsAccepting = false; + maTextType = null; + } + + + + + State Clone (final StateContext aContext) + { + return aContext.GetOrCreateState(maBasename, msSuffix); + } + + + + + static String GetStateName ( + final QualifiedName aBasename, + final String sSuffix) + { + if (sSuffix == null) + return aBasename.GetStateName(); + else + return aBasename.GetStateName()+"_"+sSuffix; + } + + + + + public String GetFullname () + { + return msFullname; + } + + + + + public QualifiedName GetBasename () + { + return maBasename; + } + + + + + /** Return a qualified name that contains the suffix. + * This is typically only used for sorting type names. + */ + public QualifiedName GetQualifiedName () + { + return new QualifiedName( + maBasename.GetNamespacePrefix(), + maBasename.GetNamespaceURI(), + msSuffix != null + ? maBasename.GetLocalPart() + "_" + msSuffix + : maBasename.GetLocalPart()); + } + + + + + public String GetSuffix () + { + return msSuffix; + } + + + + + public void AddTransition (final Transition aTransition) + { + assert(this == aTransition.GetStartState()); + maTransitions.add(aTransition); + } + + + + + public Iterable<Transition> GetTransitions() + { + return maTransitions; + } + + + + + public int GetTransitionCount () + { + return maTransitions.size(); + } + + + + + public void AddEpsilonTransition (final EpsilonTransition aTransition) + { + assert(this == aTransition.GetStartState()); + maEpsilonTransitions.add(aTransition); + } + + + + + public Iterable<EpsilonTransition> GetEpsilonTransitions() + { + return maEpsilonTransitions; + } + + + + + public void AddSkipData (final SkipData aSkipData) + { + maSkipData.add(aSkipData); + } + + + + + public Iterable<SkipData> GetSkipData () + { + return maSkipData; + } + + + + + public void SetIsAccepting () + { + mbIsAccepting = true; + } + + + + + public boolean IsAccepting () + { + return mbIsAccepting; + } + + + + + /** The basename is the primary sort key. The suffix is the secondary key. + */ + @Override + public int compareTo (final State aOther) + { + int nResult = maBasename.compareTo(aOther.maBasename); + if (nResult == 0) + { + if (msSuffix==null && aOther.msSuffix==null) + nResult = 0; + else if (msSuffix!=null && aOther.msSuffix!=null) + nResult = msSuffix.compareTo(aOther.msSuffix); + else if (msSuffix==null) + nResult = -1; + else + nResult = +1; + } + return nResult; + } + + + + + public void SetTextType (final INode aTextType) + { + assert(maTextType==null); + maTextType = aTextType; + } + + + + + public INode GetTextType () + { + return maTextType; + } + + + + + public void CopyFrom (final State aOther) + { + if (aOther.IsAccepting()) + SetIsAccepting(); + for (final SkipData aSkipData : aOther.GetSkipData()) + AddSkipData(aSkipData.Clone(this)); + SetTextType(aOther.GetTextType()); + } + + + + + @Override + public String toString () + { + return msFullname; + } + + + + + private final QualifiedName maBasename; + private final String msSuffix; + private final String msFullname; + private final Vector<Transition> maTransitions; + private final Vector<EpsilonTransition> maEpsilonTransitions; + private final Vector<SkipData> maSkipData; + private boolean mbIsAccepting; + private INode maTextType; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/StateContainer.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/StateContainer.java new file mode 100644 index 000000000000..2a8df93c96d5 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/StateContainer.java @@ -0,0 +1,73 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import java.util.HashMap; +import java.util.Map; + +/** A container of states that spans all StateContext objects that represent each + * a single complex type. + */ +public class StateContainer +{ + public StateContainer () + { + maNameToStateMap = new HashMap<>(); + } + + + + + boolean HasState (final String sFullname) + { + return maNameToStateMap.containsKey(sFullname); + } + + + + + State GetStateForFullname (final String sFullname) + { + return maNameToStateMap.get(sFullname); + } + + + + + public void AddState (final State aState) + { + maNameToStateMap.put(aState.GetFullname(), aState); + } + + + + + public void RemoveState (final State aState) + { + maNameToStateMap.remove(aState); + } + + + + + private final Map<String,State> maNameToStateMap; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/StateContext.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/StateContext.java new file mode 100644 index 000000000000..f2d877f2bdc1 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/StateContext.java @@ -0,0 +1,269 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Represents the set of states of a single complex type. + * + * Because states have to be unique, the state container is an object shared + * by all StateContext objects. + * + * There is a single start state but there can be more than one accepting state. + */ +public class StateContext +{ + public StateContext ( + final StateContainer aStateContainer, + final String sBaseStateName) + { + maStateContainer = aStateContainer; + maStates = new HashSet<>(); + maStartState = GetOrCreateState(new QualifiedName(null, null, sBaseStateName), null); + maDisambiguateCounters = new HashMap<>(); + } + + + + + public State CreateState ( + final QualifiedName aBasename, + final String sSuffix) + { + final String sFullname = State.GetStateName(aBasename, sSuffix); + if (HasState(sFullname)) + throw new RuntimeException("state with name '"+sFullname+"' can not be created because it already exists"); + final State aState = new State(aBasename, sSuffix); + AddState(aState); + return aState; + } + + + + public State CreateState (final String sBasename) + { + return CreateState(new QualifiedName(sBasename), null); + } + + + + + public State CreateState ( + final State aState, + final String sSuffix) + { + if (sSuffix==null && aState.GetSuffix()==null) + return CreateState(aState.GetBasename(), null); + else if (sSuffix!=null && aState.GetSuffix()!=null) + return CreateState(aState.GetBasename(), aState.GetSuffix()+"_"+sSuffix); + else if (sSuffix != null) + return CreateState(aState.GetBasename(), sSuffix); + else + return CreateState(aState.GetBasename(), aState.GetSuffix()); + } + + + + + public State GetState ( + final QualifiedName aBasename, + final String sSuffix) + { + return maStateContainer.GetStateForFullname(State.GetStateName(aBasename, sSuffix)); + } + + + + + public State GetOrCreateState ( + final QualifiedName aBasename, + final String sSuffix) + { + State aState = GetState(aBasename, sSuffix); + if (aState == null) + { + aState = CreateState(aBasename, sSuffix); + AddState(aState); + } + return aState; + } + + + + + public State GetStartStateForTypeName (final QualifiedName aName) + { + return GetOrCreateState(aName, null); + } + + + + + public State CreateEndState () + { + final State aEndState = CreateState( + maStartState.GetBasename(), + "end"); + aEndState.SetIsAccepting(); + return aEndState; + } + + + + /** Some algorithms can not easily produce unique suffixes. + * Append an integer to the given suffix so that it becomes unique. + */ + public String GetUnambiguousSuffix (final QualifiedName aBasename, final String sSuffix) + { + String sStateName = State.GetStateName(aBasename, sSuffix); + if ( ! HasState(sStateName)) + { + // The given suffix can be used without modification. + return sSuffix; + } + else + { + int nIndex = 2; + final Integer nDisambiguateCounter = maDisambiguateCounters.get(sStateName); + if (nDisambiguateCounter != null) + nIndex = nDisambiguateCounter+1; + maDisambiguateCounters.put(sStateName, nIndex); + + return sSuffix + "_" + nIndex; + } + } + + + + + public boolean HasState ( + final QualifiedName aBasename, + final String sSuffix) + { + return maStateContainer.HasState(State.GetStateName(aBasename, sSuffix)); + } + + + + + /** Return whether a state with the given name already belongs to the state + * context. + */ + public boolean HasState (final String sFullname) + { + return maStateContainer.HasState(sFullname); + } + + + + + /** The start state is the state a parser is in initially. + */ + public State GetStartState () + { + return maStartState; + } + + + + + public Iterable<State> GetAcceptingStates () + { + final Vector<State> aAcceptingStates = new Vector<>(); + for (final State aState : maStates) + if (aState.IsAccepting()) + aAcceptingStates.add(aState); + return aAcceptingStates; + } + + + + + /** Add the given state to the state context. + */ + public void AddState (final State aState) + { + maStateContainer.AddState(aState); + maStates.add(aState); + } + + + + + public void RemoveState (final State aState) + { + maStateContainer.RemoveState(aState); + maStates.remove(aState); + } + + + + + public int GetStateCount () + { + return maStates.size(); + } + + + + + public Iterable<State> GetStatesSorted() + { + final Set<State> aSortedStates = new TreeSet<>(); + aSortedStates.addAll(maStates); + return aSortedStates; + } + + + + + public Iterable<State> GetStates() + { + return maStates; + } + + + + + public int GetTransitionCount () + { + int nStateCount = 0; + for (final State aState : maStates) + nStateCount += aState.GetTransitionCount(); + return nStateCount; + } + + + + + private final StateContainer maStateContainer; + private final Set<State> maStates; + private final State maStartState; + private final Map<String,Integer> maDisambiguateCounters; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/StateSet.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/StateSet.java new file mode 100644 index 000000000000..bac2092104fe --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/StateSet.java @@ -0,0 +1,245 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Used in the transformation of NFA to DFA and in the minimization of DFAs. + * References a set of regular states. + */ +public class StateSet + implements Comparable<StateSet> +{ + public StateSet () + { + maStates = new TreeSet<>(); + } + + + + + public StateSet (final State aState) + { + this(); + maStates.add(aState); + } + + + + + public StateSet (final StateSet aSet) + { + this(); + maStates.addAll(aSet.maStates); + } + + + + + public StateSet (final Iterable<State> aStates) + { + this(); + for (final State aState : aStates) + maStates.add(aState); + } + + + + + public void AddState (final State aState) + { + maStates.add(aState); + } + + + + + public void AddStates (final StateSet aStates) + { + maStates.addAll(aStates.maStates); + } + + + + + public boolean IsDisjoint (final StateSet aOther) + { + for (final State aState : aOther.maStates) + if (maStates.contains(aState)) + return false; + for (final State aState : maStates) + if (aOther.maStates.contains(aState)) + return false; + return true; + } + + + + + public void RemoveState (final State aState) + { + maStates.remove(aState); + } + + + + + public Iterable<State> GetStates () + { + return maStates; + } + + + + + public boolean ContainsState (final State aState) + { + return maStates.contains(aState); + } + + + + + public int GetStateCount () + { + return maStates.size(); + } + + + + + public boolean HasStates () + { + return ! maStates.isEmpty(); + } + + + + + public State CreateStateForStateSet (final StateContext aContext) + { + // Find a name for the new state. If there is type state in the given + // set then use its name. + QualifiedName aBaseName = null; + String sShortestSuffix = null; + for (final State aState : maStates) + { + final QualifiedName aName = aState.GetBasename(); + final String sSuffix = aState.GetSuffix(); + + if (aBaseName == null) + { + aBaseName = aName; + sShortestSuffix = sSuffix; + } + else if (aBaseName.compareTo(aName) != 0) + { + System.out.printf("%s != %s\n", aBaseName, aName); + throw new RuntimeException("state set contains states with different base names: "+toString()); + } + + if (sShortestSuffix == null) + sShortestSuffix = sSuffix; + else if (sSuffix.length() < sShortestSuffix.length()) + sShortestSuffix = sSuffix; + } + if (aBaseName == null) + throw new RuntimeException("can not create state for "+toString()); + + // Disambiguate new state name. + State aNewState = aContext.CreateState( + aBaseName, + aContext.GetUnambiguousSuffix(aBaseName, sShortestSuffix)); + assert(aNewState!=null); + + // Mark the new state as accepting if at least one of its original states + // is accepting. + for (final State aState : maStates) + { + if (aState.IsAccepting()) + { + aNewState.SetIsAccepting(); + break; + } + for (final SkipData aData : aState.GetSkipData()) + aNewState.AddSkipData(aData.Clone(aNewState)); + final INode aTextType = aState.GetTextType(); + if (aTextType != null) + aNewState.SetTextType(aTextType); + } + + return aNewState; + } + + + + + @Override + public int compareTo (final StateSet aOther) + { + final int nStateCount = maStates.size(); + + if (nStateCount != aOther.maStates.size()) + return nStateCount - aOther.maStates.size(); + else + { + final Iterator<State> aIterator = maStates.iterator(); + final Iterator<State> aOtherIterator = aOther.maStates.iterator(); + while (aIterator.hasNext()) + { + final State aState = aIterator.next(); + final State aOtherState = aOtherIterator.next(); + final int nResult = aState.compareTo(aOtherState); + if (nResult != 0) + return nResult; + } + return 0; + } + } + + + + + public boolean IsEmpty() + { + return maStates.isEmpty(); + } + + + + + @Override + public String toString () + { + return "set of "+maStates.size()+" states "+maStates.toString(); + } + + + + + private final Set<State> maStates; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/Transition.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/Transition.java new file mode 100644 index 000000000000..ffacb1ad5ef9 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/Transition.java @@ -0,0 +1,97 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Each transition corresponds to an 'element' schema element. + * It moves from the start state to the end state when a certain start tag is + * processed. The element corresponds to another complex type which will be + * parsed during the transition. + */ +public class Transition +{ + public Transition ( + final State aStartState, + final State aEndState, + final QualifiedName aElementName, + final String sElementTypeName) + { + maStartState = aStartState; + maEndState = aEndState; + maElementName = aElementName; + msElementTypeName = sElementTypeName; + } + + + + + public State GetStartState () + { + return maStartState; + } + + + + + public State GetEndState () + { + return maEndState; + } + + + + + public QualifiedName GetElementName() + { + return maElementName; + } + + + + + public String GetElementTypeName() + { + return msElementTypeName; + } + + + + + @Override + public String toString () + { + return String.format("%s --'%s'-> %s (via %s)\n", + maStartState.GetFullname(), + maElementName.GetDisplayName(), + maEndState.GetFullname(), + msElementTypeName); + } + + + + + private final State maStartState; + private final State maEndState; + private final QualifiedName maElementName; + private final String msElementTypeName; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/ValidatingCreator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/ValidatingCreator.java new file mode 100644 index 000000000000..c0dd5acf1781 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/automaton/ValidatingCreator.java @@ -0,0 +1,793 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.automaton; + +import java.io.File; +import java.util.Iterator; +import java.util.Stack; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.iterator.DereferencingNodeIterator; +import org.apache.openoffice.ooxml.schema.iterator.PermutationIterator; +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.NodeVisitorAdapter; +import org.apache.openoffice.ooxml.schema.model.complex.All; +import org.apache.openoffice.ooxml.schema.model.complex.Any; +import org.apache.openoffice.ooxml.schema.model.complex.Choice; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexContent; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexType; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexTypeReference; +import org.apache.openoffice.ooxml.schema.model.complex.Element; +import org.apache.openoffice.ooxml.schema.model.complex.ElementReference; +import org.apache.openoffice.ooxml.schema.model.complex.Extension; +import org.apache.openoffice.ooxml.schema.model.complex.Group; +import org.apache.openoffice.ooxml.schema.model.complex.GroupReference; +import org.apache.openoffice.ooxml.schema.model.complex.OccurrenceIndicator; +import org.apache.openoffice.ooxml.schema.model.complex.Sequence; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; +import org.apache.openoffice.ooxml.schema.model.simple.BuiltIn; +import org.apache.openoffice.ooxml.schema.model.simple.List; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleContent; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleTypeReference; +import org.apache.openoffice.ooxml.schema.model.simple.Union; + +/** Create a set of validating stack automatons for a set of schemas. + * There is one DFA (deterministic finite automaton) for each complex type and + * one for the top level elements. + */ +public class ValidatingCreator + extends CreatorBase + implements INodeVisitor +{ + public ValidatingCreator ( + final SchemaBase aSchemaBase, + final File aLogFile) + { + super(aSchemaBase, aLogFile); + maContextStack = new Stack<>(); + maCurrentContext = null; + } + + + + + /** Create one automaton for the top-level elements and one for each complex + * type. + */ + public FiniteAutomatonContainer Create () + { + final FiniteAutomatonContainer aAutomatons = new FiniteAutomatonContainer(maStateContainer); + + // Create the automaton for the top-level elements. + aAutomatons.AddAutomaton( + null, + CreateForTopLevelElements()); + + // Create one automation for each complex type. + for (final ComplexType aComplexType : maSchemaBase.ComplexTypes.GetSorted()) + aAutomatons.AddAutomaton( + aComplexType.GetName(), + CreateForComplexType(aComplexType)); + + // Create one automaton for each simple type that is referenced by an element. + for (final INode aSimpleType : maElementSimpleTypes) + aAutomatons.AddAutomaton( + aSimpleType.GetName(), + CreateForSimpleType(aSimpleType)); + + maLog.Close(); + + return aAutomatons; + } + + + + + private FiniteAutomaton CreateForTopLevelElements () + { + maStateContext = new StateContext( + maStateContainer, + "<top-level>"); + final State aEndState = maStateContext.CreateEndState(); + + assert(maContextStack.isEmpty()); + msLogIndentation = ""; + + // top level elements + for (final Element aElement : maSchemaBase.TopLevelElements.GetSorted()) + ProcessType( + aElement, + maStateContext.GetStartState(), + maStateContext.GetStartState(), + aEndState); + + return new FiniteAutomaton(maStateContext, null, null); + } + + + + + private FiniteAutomaton CreateForComplexType (final ComplexType aComplexType) + { + maStateContext = new StateContext( + maStateContainer, + aComplexType.GetName().GetStateName()); + maAttributes = new Vector<>(); + final State aEndState = maStateContext.CreateEndState(); + ProcessType( + aComplexType, + maStateContext.GetStartState(), + maStateContext.GetStartState(), + aEndState); + return new FiniteAutomaton( + maStateContext, + maAttributes, + aComplexType.GetLocation()); + } + + + + + @Override + public void Visit (final All aAll) + { + maLog.AddComment("All"); + ProcessAttributes(aAll); + + // Make a transformation of the children into a choice of sequences that + // can then be processed by already existing Visit() methods. + // These sequences enumerate all permutations of the original children. + final INode aReplacement = GetAllReplacement(aAll); + + final State aLocalStartState = maStateContext.CreateState( + maCurrentContext.BaseState, + "As"); + final State aLocalEndState = maStateContext.CreateState( + maCurrentContext.BaseState, + "Ae"); + + maLog.StartBlock(); + AddEpsilonTransition(maCurrentContext.StartState, aLocalStartState); + final long nStartTime = System.currentTimeMillis(); + ProcessType( + aReplacement, + maStateContext.CreateState(maCurrentContext.BaseState, "A"), + aLocalStartState, + aLocalEndState); + final long nEndTime = System.currentTimeMillis(); + System.out.printf("processed 'all' children in %fs\n", (nEndTime-nStartTime)/1000.0); + AddEpsilonTransition(aLocalEndState, maCurrentContext.EndState); + maLog.EndBlock(); + } + + + + + @Override + public void Visit (final Any aAny) + { + assert(aAny.GetChildCount() == 0); + + maLog.AddComment("Any"); + ProcessAttributes(aAny); + + AddSkipTransition( + maCurrentContext.StartState, + new SkipData( + aAny.GetProcessContentsFlag(), + aAny.GetNamespaces())); + AddEpsilonTransition(maCurrentContext.StartState, maCurrentContext.EndState); + } + + + + + @Override + public void Visit (final ComplexContent aComplexContent) + { + assert(aComplexContent.GetChildCount() == 1); + + maLog.AddComment ("Complex Content."); + ProcessAttributes(aComplexContent); + + maLog.StartBlock(); + ProcessType( + aComplexContent.GetChildren().iterator().next(), + maCurrentContext.BaseState, + maCurrentContext.StartState, + maCurrentContext.EndState); + maLog.EndBlock(); + } + + + + + @Override + public void Visit (final ComplexType aComplexType) + { + if (maLog != null) + { + maLog.printf("\n"); + maLog.AddComment ("Complex Type %s defined in %s.", + aComplexType.GetName().GetDisplayName(), + aComplexType.GetLocation()); + } + ProcessAttributes(aComplexType); + + maLog.StartBlock(); + maLog.printf("%sstarting at state %s\n", msLogIndentation, maCurrentContext.StartState.GetFullname()); + + if (GetElementCount(aComplexType) == 0) + { + // There are elements. Therefore there will be no transitions. + // The start state is accepting and the end state is not necessary. + maCurrentContext.StartState.SetIsAccepting(); + maStateContext.RemoveState(maCurrentContext.EndState); + } + + for (final INode aChild : aComplexType.GetChildren()) + ProcessType(aChild, maCurrentContext.BaseState, maCurrentContext.StartState, maCurrentContext.EndState); + + maLog.EndBlock(); + } + + + + + @Override + public void Visit (final ComplexTypeReference aNode) + { + throw new RuntimeException("can not handle "+aNode.toString()); + } + + + + + @Override + public void Visit (final Choice aChoice) + { + maLog.AddComment("Choice"); + ProcessAttributes(aChoice); + + final State aLocalStartState = maStateContext.CreateState(maCurrentContext.BaseState, "Cs"); + final State aLocalEndState = maStateContext.CreateState(maCurrentContext.BaseState, "Ce"); + maLog.StartBlock(); + AddEpsilonTransition(maCurrentContext.StartState, aLocalStartState); + + int nStateIndex = 0; + for (final INode aChild : aChoice.GetChildren()) + { + ProcessType( + aChild, + maStateContext.CreateState(maCurrentContext.BaseState, "C"+nStateIndex++), + aLocalStartState, + aLocalEndState); + } + AddEpsilonTransition(aLocalEndState, maCurrentContext.EndState); + maLog.EndBlock(); + } + + + + + @Override + public void Visit (final Element aElement) + { + assert(aElement.GetChildCount()==0); + + maLog.AddComment("Element: on '%s' go from %s to %s via %s", + aElement.GetElementName().GetDisplayName(), + maCurrentContext.StartState.GetFullname(), + maCurrentContext.EndState.GetFullname(), + aElement.GetTypeName().GetStateName()); + ProcessAttributes(aElement); + + final Transition aTransition = new Transition( + maCurrentContext.StartState, + maCurrentContext.EndState, + aElement.GetElementName(), + aElement.GetTypeName().GetStateName()); + maCurrentContext.StartState.AddTransition(aTransition); + + // For elements whose type is a simple type we have to remember that + // simple type for later (and then create an NFA for it.) + final INode aSimpleType = maSchemaBase.GetSimpleTypeForName( + aElement.GetTypeName()); + if (aSimpleType != null) + maElementSimpleTypes.add(aSimpleType); + } + + + + + @Override + public void Visit (final ElementReference aReference) + { + assert(aReference.GetChildCount() == 0); + + maLog.AddComment("Element reference to %s", aReference.GetReferencedElementName()); + ProcessAttributes(aReference); + + final Element aElement = aReference.GetReferencedElement(maSchemaBase); + if (aElement == null) + throw new RuntimeException("can't find referenced element "+aReference.GetReferencedElementName()); + maLog.StartBlock(); + ProcessType(aElement, maCurrentContext.BaseState, maCurrentContext.StartState, maCurrentContext.EndState); + maLog.EndBlock(); + } + + + + + /** Treat extension nodes like sequences (for now). + */ + @Override + public void Visit (final Extension aExtension) + { + assert(aExtension.GetChildCount() <= 1); + + maLog.AddComment("Extension of base type %s", aExtension.GetBaseTypeName()); + ProcessAttributes(aExtension); + + final Vector<INode> aNodes = aExtension.GetTypeNodes(maSchemaBase); + + maLog.StartBlock(); + int nStateIndex = 0; + State aCurrentState = maStateContext.CreateState(maCurrentContext.BaseState, "E"+nStateIndex++); + AddEpsilonTransition(maCurrentContext.StartState, aCurrentState); + + State aNextState = maStateContext.CreateState(maCurrentContext.BaseState, "E"+nStateIndex++); + ProcessType(aExtension.GetReferencedNode(maSchemaBase), aCurrentState, aCurrentState, aNextState); + aCurrentState = aNextState; + + for (final INode aChild : aNodes) + { + aNextState = maStateContext.CreateState(maCurrentContext.BaseState, "E"+nStateIndex++); + ProcessType(aChild, aCurrentState, aCurrentState, aNextState); + aCurrentState = aNextState; + } + AddEpsilonTransition(aCurrentState, maCurrentContext.EndState); + maLog.EndBlock(); + } + + + + + @Override + public void Visit (final Group aGroup) + { + assert(aGroup.GetChildCount() == 1); + + maLog.AddComment("Group %s", aGroup.GetName()); + ProcessAttributes(aGroup); + + maLog.StartBlock(); + final State aGroupBaseState = maStateContext.CreateState(maCurrentContext.BaseState, "G"); + ProcessType( + aGroup.GetOnlyChild(), + aGroupBaseState, + maCurrentContext.StartState, + maCurrentContext.EndState); + maLog.EndBlock(); + } + + + + + @Override + public void Visit (final GroupReference aReference) + { + maLog.AddComment("Group reference to %s", aReference.GetReferencedGroupName()); + ProcessAttributes(aReference); + + final Group aGroup = aReference.GetReferencedGroup(maSchemaBase); + if (aGroup == null) + throw new RuntimeException("can't find referenced group "+aReference.GetReferencedGroupName()); + + maLog.StartBlock(); + ProcessType(aGroup, maCurrentContext.BaseState, maCurrentContext.StartState, maCurrentContext.EndState); + maLog.EndBlock(); + } + + + + + /** An occurrence indicator defines how many times the single child can occur. + * The minimum value defines the mandatory number of times. The maximum value + * defines the optional number. + */ + @Override + public void Visit (final OccurrenceIndicator aOccurrence) + { + assert(aOccurrence.GetChildCount() == 1); + + maLog.AddComment("OccurrenceIndicator %s->%s", + aOccurrence.GetDisplayMinimum(), + aOccurrence.GetDisplayMaximum()); + ProcessAttributes(aOccurrence); + + maLog.StartBlock(); + + final INode aChild = aOccurrence.GetChildren().iterator().next(); + + int nIndex = 0; + State aCurrentState = maStateContext.CreateState(maCurrentContext.BaseState, "O"+nIndex++); + AddEpsilonTransition(maCurrentContext.StartState, aCurrentState); + + if (aOccurrence.GetMinimum() == 0) + { + // A zero minimum means that all occurrences are optional. + // Add a short circuit from start to end. + maLog.AddComment("Occurrence: make whole element optional (min==0)"); + AddEpsilonTransition(maCurrentContext.StartState, maCurrentContext.EndState); + } + else + { + // Write a row of mandatory transitions for the minimum. + for (; nIndex<=aOccurrence.GetMinimum(); ++nIndex) + { + // Add transition i-1 -> i (i == nIndex). + final State aNextState = maStateContext.CreateState(maCurrentContext.BaseState, "O"+nIndex); + maLog.AddComment("Occurrence: move from %d -> %d (%s -> %s) (minimum)", + nIndex-1, + nIndex, + aCurrentState, + aNextState); + maLog.StartBlock(); + ProcessType(aChild, aCurrentState, aCurrentState, aNextState); + maLog.EndBlock(); + aCurrentState = aNextState; + } + } + + if (aOccurrence.GetMaximum() == OccurrenceIndicator.unbounded) + { + // Write loop on last state when max is unbounded. + + // last -> loop + final State aLoopState = maStateContext.CreateState(maCurrentContext.BaseState, "OL"); + maLog.AddComment("Occurrence: forward to loop (maximum)"); + AddEpsilonTransition(aCurrentState, aLoopState); + + // loop -> loop + maLog.AddComment("Occurrence: loop"); + maLog.StartBlock(); + ProcessType(aChild, aLoopState, aLoopState, aLoopState); + maLog.EndBlock(); + + // -> end + maLog.AddComment("Occurrence: forward to local end"); + AddEpsilonTransition(aLoopState, maCurrentContext.EndState); + } + else + { + // Write a row of optional transitions for the maximum. + for (; nIndex<=aOccurrence.GetMaximum(); ++nIndex) + { + if (nIndex > 0) + { + // i-1 -> end + maLog.AddComment("Occurrence: make %d optional (maximum)", nIndex-1); + AddEpsilonTransition(aCurrentState, maCurrentContext.EndState); + } + + // i-1 -> i + final State aNextState = maStateContext.CreateState(maCurrentContext.BaseState, "O"+nIndex); + maLog.AddComment("Occurrence: %d -> %d (%s -> %s) (maximum)", + nIndex-1, + nIndex, + aCurrentState, + aNextState); + maLog.StartBlock(); + ProcessType(aChild, aCurrentState, aCurrentState, aNextState); + maLog.EndBlock(); + + aCurrentState = aNextState; + } + + // max -> end + maLog.AddComment("Occurrence: forward to local end"); + AddEpsilonTransition(aCurrentState, maCurrentContext.EndState); + } + maLog.EndBlock(); + } + + + + + /** Ordered sequence of nodes. + * For n nodes create states S0 to Sn where Si and Si+1 become start and + * end states for the i-th child. + */ + @Override + public void Visit (final Sequence aSequence) + { + maLog.AddComment("Sequence."); + ProcessAttributes(aSequence); + + maLog.StartBlock(); + int nStateIndex = 0; + State aCurrentState = maStateContext.CreateState(maCurrentContext.BaseState, "S"+nStateIndex++); + AddEpsilonTransition(maCurrentContext.StartState, aCurrentState); + for (final INode aChild : aSequence.GetChildren()) + { + final State aNextState = maStateContext.CreateState(maCurrentContext.BaseState, "S"+nStateIndex++); + ProcessType(aChild, aCurrentState, aCurrentState, aNextState); + aCurrentState = aNextState; + } + AddEpsilonTransition(aCurrentState, maCurrentContext.EndState); + maLog.EndBlock(); + } + + + + + @Override + public void Visit (final BuiltIn aNode) + { + // Ignored. + //throw new RuntimeException("can not handle "+aNode.toString()); + } + + + + + @Override + public void Visit (final List aNode) + { + throw new RuntimeException("can not handle "+aNode.toString()); + } + + + + + @Override + public void Visit (final Restriction aNode) + { + throw new RuntimeException("can not handle "+aNode.toString()); + } + + + + @Override + public void Visit (final SimpleContent aNode) + { + maLog.AddComment("SimpleContent."); + ProcessAttributes(aNode); + + for (final INode aChild : aNode.GetChildren()) + ProcessType(aChild, maCurrentContext.BaseState, maCurrentContext.StartState, maCurrentContext.EndState); + } + + + + + @Override + public void Visit (final SimpleType aNode) + { + maLog.AddComment("SimpleType."); + //for (final INode aChild : aNode.GetChildren()) + //ProcessType(aChild, maCurrentContext.BaseState, maCurrentContext.StartState, maCurrentContext.EndState); + } + + + + + @Override + public void Visit (final SimpleTypeReference aNode) + { + throw new RuntimeException("can not handle "+aNode.toString()); + } + + + + + @Override + public void Visit (final Union aNode) + { + throw new RuntimeException("can not handle "+aNode.toString()); + } + + + + + @Override + public void Visit (final AttributeGroup aNode) + { + throw new RuntimeException("can not handle "+aNode.toString()); + } + + + + + @Override + public void Visit (final AttributeReference aNode) + { + throw new RuntimeException("can not handle "+aNode.toString()); + } + + + + + @Override + public void Visit (final Attribute aNode) + { + throw new RuntimeException("can not handle "+aNode.toString()); + } + + + + + @Override + public void Visit (final AttributeGroupReference aNode) + { + throw new RuntimeException("can not handle "+aNode.toString()); + } + + + + + private void ProcessType ( + final INode aNode, + final State aBaseState, + final State aStartState, + final State aEndState) + { + maContextStack.push(maCurrentContext); + maCurrentContext = new Context(aBaseState, aStartState, aEndState); + aNode.AcceptVisitor(this); + maCurrentContext = maContextStack.pop(); + } + + + + + private void AddEpsilonTransition ( + final State aStartState, + final State aEndState) + { + // Silently ignore epsilon transitions from a state to itself. + // They may indicate a problem but usually are just artifacts + // that can be safely ignored. + if (aStartState == aEndState) + return; + else + { + final EpsilonTransition aTransition = new EpsilonTransition( + aStartState, + aEndState); + aStartState.AddEpsilonTransition(aTransition); + + if (maLog != null) + { + maLog.printf("%sepsilon transition from %s to %s\n", + msLogIndentation, + aStartState.GetFullname(), + aEndState.GetFullname()); + } + } + } + + + + + private int GetElementCount (final INode aNode) + { + + class Visitor extends NodeVisitorAdapter + { + int nElementCount = 0; + @Override public void Visit (final Element aElement) + { + ++nElementCount; + } + int GetElementCount () + { + return nElementCount; + } + }; + final Visitor aVisitor = new Visitor(); + for (final INode aChildNode : new DereferencingNodeIterator(aNode, maSchemaBase, false)) + { + aChildNode.AcceptVisitor(aVisitor); + } + return aVisitor.GetElementCount(); + } + + + + + private INode GetAllReplacement (final All aAll) + { + final long nStartTime = System.currentTimeMillis(); + + // By default each child of this node can appear exactly once, however + // the order is undefined. This corresponds to an enumeration of all + // permutations of the children. + + // Set up an array of all children. This array will be modified to contain + // all permutations. + final INode[] aNodes = new INode[aAll.GetChildCount()]; + final Iterator<INode> aChildren = aAll.GetChildren().iterator(); + for (int nIndex=0; aChildren.hasNext(); ++nIndex) + aNodes[nIndex] = aChildren.next(); + + final Location aLocation = aAll.GetLocation(); + final Choice aChoice = new Choice(aAll, aLocation); + + // Treat every permutation as sequence so that the whole set of permutations + // is equivalent to a choice of sequences. + int nCount = 0; + for (final PermutationIterator<INode> aIterator = new PermutationIterator<>(aNodes); aIterator.HasMore(); aIterator.Next()) + { + // Create a Sequence node for the current permutation and add it as + // choice to the Choice node. + final Sequence aSequence = new Sequence(aChoice, null, aLocation); + aChoice.AddChild(aSequence); + + for (final INode aNode : aNodes) + aSequence.AddChild(aNode); + + ++nCount; + } + final long nEndTime = System.currentTimeMillis(); + System.out.printf("created %d permutations in %fs\n", + nCount, + (nEndTime-nStartTime)/1000.0); + + return aChoice; + } + + + + + class Context + { + Context ( + final State aBaseState, + final State aStartState, + final State aEndState) + { + BaseState = aBaseState; + StartState = aStartState; + EndState = aEndState; + } + final State BaseState; + final State StartState; + final State EndState; + } + + + + + private StateContext maStateContext; + private final Stack<Context> maContextStack; + private Context maCurrentContext; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/LogGenerator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/LogGenerator.java new file mode 100644 index 000000000000..36deab269b96 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/LogGenerator.java @@ -0,0 +1,313 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.generator; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.util.Map.Entry; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexType; +import org.apache.openoffice.ooxml.schema.model.complex.Element; +import org.apache.openoffice.ooxml.schema.model.schema.Schema; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleTypeReference; + +public class LogGenerator +{ + public static void Write ( + final File aOutputFile, + final SchemaBase aSchemaBase, + final Iterable<Schema> aTopLevelSchemas) + { + final long nStartTime = System.currentTimeMillis(); + + try + { + final LogGenerator aGenerator = new LogGenerator( + new PrintStream(aOutputFile), + aSchemaBase); + + aGenerator.WriteNamespaces(aSchemaBase); + aGenerator.WriteTopLevelElements(aTopLevelSchemas); + aGenerator.WriteComplexTypes(aSchemaBase); + aGenerator.WriteGroups(aSchemaBase); + aGenerator.WriteSimpleTypes(aSchemaBase); + aGenerator.WriteAttributeGroups(aSchemaBase); + aGenerator.WriteAttributes(aSchemaBase); + } + catch (final FileNotFoundException aException) + { + aException.printStackTrace(); + } + + final long nEndTime = System.currentTimeMillis(); + System.out.printf("wrote log output to '%s' in %fs\n", + aOutputFile.toString(), + (nEndTime-nStartTime)/1000.0f); + } + + + + + private LogGenerator ( + final PrintStream aOut, + final SchemaBase aSchemaBase) + { + maSchemaBase = aSchemaBase; + maOut = aOut; + } + + + + + private void WriteComment (final String sFormat, final Object ... aArgumentList) + { + maOut.printf("// "+sFormat+"\n", aArgumentList); + } + + + + + private void WriteNamespaces (final SchemaBase aSchema) + { + // Write namespace definitions. + WriteComment("%d Namespaces.", aSchema.Namespaces.GetCount()); + for (final Entry<String,String> aEntry : aSchema.Namespaces.GetSorted()) + { + maOut.printf(" %s -> %s\n", + aEntry.getValue()==null ? "<no-prefix>" : aEntry.getValue(), + aEntry.getKey()); + } + } + + + + private void WriteTopLevelElements (final Iterable<Schema> aTopLevelSchemas) + { + // Write top level elements. + WriteComment("Top-level elements."); + for (final Schema aSchema : aTopLevelSchemas) + { + WriteComment(" Schema %s.", aSchema.GetShortName()); + for (final Element aElement : aSchema.TopLevelElements.GetSorted()) + maOut.printf(" \"%s\" -> %s\n", + aElement.GetElementName().GetDisplayName(), + aElement.GetTypeName().GetDisplayName()); + } + } + + + + + private void WriteComplexTypes (final SchemaBase aSchema) + { + WriteComment(" %d Complex Types.", aSchema.ComplexTypes.GetCount()); + for (final ComplexType aType : aSchema.ComplexTypes.GetSorted()) + { + WriteType(" ", aType, true); + } + } + + + + + private void WriteSimpleTypes (final SchemaBase aSchema) + { + WriteComment(" %d Simple Types.", aSchema.SimpleTypes.GetCount()); + for (final SimpleType aType : aSchema.SimpleTypes.GetSorted()) + { + WriteType(" ", aType, true); + } + } + + + + + private void WriteGroups (final SchemaBase aSchema) + { + WriteComment(" %d Groups.", aSchema.Groups.GetCount()); + for (final Node aType : aSchema.Groups.GetSorted()) + { + WriteType(" ", aType, true); + } + } + + + + + private void WriteAttributeGroups (final SchemaBase aSchema) + { + WriteComment(" %d Attribute Groups.", aSchema.AttributeGroups.GetCount()); + for (final Node aType : aSchema.AttributeGroups.GetSorted()) + { + WriteType(" ", aType, true); + } + } + + + + + private void WriteAttributes (final SchemaBase aSchema) + { + WriteComment(" %d Attributes.", aSchema.Attributes.GetCount()); + for (final Node aType : aSchema.Attributes.GetSorted()) + { + WriteType(" ", aType, true); + } + } + + + + + private void WriteType ( + final String sIndentation, + final INode aType, + final boolean bIsTopLevel) + { + maOut.printf("%s%s", sIndentation, aType.toString()); + + if (bIsTopLevel) + { + final Node aNode = (Node)aType; + maOut.printf(" defined at %s", + aNode.GetLocation()); + } + if ( ! HasChild(aType)) + { + maOut.printf(" {}\n"); + } + else + { + maOut.printf(" {\n"); + + // Write attributes. + switch(aType.GetNodeType()) + { + case ComplexType: + for (final INode aAttribute : ((ComplexType)aType).GetAttributes()) + WriteAttribute(sIndentation+" ", aAttribute); + break; + + case SimpleTypeReference: + WriteType( + sIndentation+" ", + ((SimpleTypeReference)aType).GetReferencedSimpleType(maSchemaBase), + false); + break; + + default: + break; + } + + + // Write child types. + for (final INode aChild : aType.GetChildren()) + WriteType(sIndentation+" ", aChild, false); + + maOut.printf("%s}\n", sIndentation); + } + } + + + + + private void WriteAttribute ( + final String sIndentation, + final INode aAttribute) + { + switch(aAttribute.GetNodeType()) + { + case Attribute: + maOut.printf( + "%sattribute %s of type %s\n", + sIndentation, + ((Attribute)aAttribute).GetName().GetDisplayName(), + ((Attribute)aAttribute).GetTypeName().GetDisplayName()); + break; + + case AttributeGroup: + maOut.printf( + "%sattribute group %s {\n", + sIndentation, + ((AttributeGroup)aAttribute).GetName().GetDisplayName()); + for (final INode aChildAttribute : ((AttributeGroup)aAttribute).GetChildren()) + WriteAttribute(sIndentation+" ", aChildAttribute); + maOut.printf("%s}\n", sIndentation); + break; + case AttributeGroupReference: + maOut.printf( + "%sreference to attribute group %s {\n", + sIndentation, + ((AttributeGroupReference)aAttribute).GetReferencedName().GetDisplayName()); + WriteAttribute(sIndentation+" ", ((AttributeGroupReference)aAttribute).GetReferencedAttributeGroup(maSchemaBase)); + maOut.printf("%s}\n", sIndentation); + break; + + case AttributeReference: + maOut.printf( + "%sreference to attribute %s {\n", + sIndentation, + ((AttributeReference)aAttribute).GetReferencedName().GetDisplayName()); + WriteAttribute(sIndentation+" ", ((AttributeReference)aAttribute).GetReferencedAttribute(maSchemaBase)); + maOut.printf("%s}\n", sIndentation); + break; + default: + throw new RuntimeException(); + } + } + + + + + private boolean HasChild (final INode aType) + { + if (aType.GetChildren().iterator().hasNext()) + return true; + + switch (aType.GetNodeType()) + { + case ComplexType: + return ((ComplexType)aType).GetAttributes().iterator().hasNext(); + + case SimpleTypeReference: + return true; + + default: + return false; + } + } + + + + + private final SchemaBase maSchemaBase; + private final PrintStream maOut; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/ParserTablesGenerator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/ParserTablesGenerator.java new file mode 100644 index 000000000000..99cfbe1161bb --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/ParserTablesGenerator.java @@ -0,0 +1,555 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.generator; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.apache.openoffice.ooxml.schema.automaton.FiniteAutomaton; +import org.apache.openoffice.ooxml.schema.automaton.FiniteAutomatonContainer; +import org.apache.openoffice.ooxml.schema.automaton.SkipData; +import org.apache.openoffice.ooxml.schema.automaton.State; +import org.apache.openoffice.ooxml.schema.automaton.Transition; +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeBase.Use; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.schema.NamespaceMap; +import org.apache.openoffice.ooxml.schema.parser.FormDefault; +import org.apache.openoffice.ooxml.schema.simple.BlobNode; +import org.apache.openoffice.ooxml.schema.simple.DateTimeNode; +import org.apache.openoffice.ooxml.schema.simple.ISimpleTypeNode; +import org.apache.openoffice.ooxml.schema.simple.ISimpleTypeNodeVisitor; +import org.apache.openoffice.ooxml.schema.simple.NumberNode; +import org.apache.openoffice.ooxml.schema.simple.SimpleTypeContainer; +import org.apache.openoffice.ooxml.schema.simple.SimpleTypeDescriptor; +import org.apache.openoffice.ooxml.schema.simple.StringNode; +import org.apache.openoffice.ooxml.schema.simple.UnionNode; + +public class ParserTablesGenerator +{ + public ParserTablesGenerator ( + final FiniteAutomatonContainer aAutomatons, + final NamespaceMap aNamespaces, + final SimpleTypeContainer aSimpleTypes, + final Map<String,Integer> aAttributeValueToIdMap) + { + maAutomatons = aAutomatons; + maSimpleTypes = aSimpleTypes; + maNamespaces = aNamespaces; + maNameToIdMap = new TreeMap<>(); + maPrefixToIdMap = new HashMap<>(); + maTypeNameToIdMap = new TreeMap<>(); + maAttributeValueToIdMap = aAttributeValueToIdMap; + } + + + + + public void Generate ( + final File aParseTableFile) + { + final long nStartTime = System.currentTimeMillis(); + + SetupNameList(); + AssignNameIds(); + + try + { + final PrintStream aOut = new PrintStream(new FileOutputStream(aParseTableFile)); + + WriteNamespaceList(aOut); + WriteNameList(aOut); + WriteGlobalStartEndStates(aOut); + WriteAutomatonList(aOut); + WriteSimpleTypes(aOut); + WriteAttributeValues(aOut); + aOut.close(); + } + catch (final FileNotFoundException aException) + { + aException.printStackTrace(); + } + + final long nEndTime = System.currentTimeMillis(); + System.out.printf("wrote parse tables to %s in %fs\n", + aParseTableFile.toString(), + (nEndTime-nStartTime)/1000.0); + } + + + + + private void SetupNameList () + { + final Set<String> aNames = new TreeSet<>(); + + // Add the element names. + for (final FiniteAutomaton aAutomaton : maAutomatons.GetAutomatons()) + for (final Transition aTransition : aAutomaton.GetTransitions()) + { + if (aTransition.GetElementName() == null) + throw new RuntimeException(); + aNames.add(aTransition.GetElementName().GetLocalPart()); + } + + // Add the attribute names. + for (final FiniteAutomaton aAutomaton : maAutomatons.GetAutomatons()) + for (final Attribute aAttribute : aAutomaton.GetAttributes()) + aNames.add(aAttribute.GetName().GetLocalPart()); + + // Create unique ids for the names. + int nIndex = 1; + maNameToIdMap.clear(); + for (final String sName : aNames) + maNameToIdMap.put(sName, nIndex++); + + // Create unique ids for namespace prefixes. + nIndex = 1; + maPrefixToIdMap.clear(); + for (final Entry<String, String> aEntry : maNamespaces) + { + maPrefixToIdMap.put(aEntry.getValue(), nIndex++); + } + } + + + + + /** During the largest part of the parsing process, states and elements are + * identified not via their name but via a unique id. + * That allows a fast lookup. + */ + private void AssignNameIds () + { + maTypeNameToIdMap.clear(); + int nIndex = 0; + + // Process state names. + final Set<QualifiedName> aSortedTypeNames = new TreeSet<>(); + for (final State aState : maAutomatons.GetStates()) + aSortedTypeNames.add(aState.GetQualifiedName()); + for (final Entry<String, SimpleTypeDescriptor> aSimpleType : maSimpleTypes.GetSimpleTypes()) + aSortedTypeNames.add(aSimpleType.getValue().GetName()); + + for (final QualifiedName aName : aSortedTypeNames) + maTypeNameToIdMap.put(aName.GetStateName(), nIndex++); + } + + + + + private void WriteNamespaceList (final PrintStream aOut) + { + aOut.printf("# namespaces\n"); + for (final Entry<String, String> aEntry : maNamespaces) + { + aOut.printf("namespace %-8s %2d %s\n", + aEntry.getValue(), + maPrefixToIdMap.get(aEntry.getValue()), + aEntry.getKey()); + } + } + + + + + private void WriteGlobalStartEndStates (final PrintStream aOut) + { + aOut.printf("\n# start and end states\n"); + + final FiniteAutomaton aAutomaton = maAutomatons.GetTopLevelAutomaton(); + final State aStartState = aAutomaton.GetStartState(); + aOut.printf("start-state %4d %s\n", + maTypeNameToIdMap.get(aStartState.GetFullname()), + aStartState.GetFullname()); + for (final State aAcceptingState : aAutomaton.GetAcceptingStates()) + aOut.printf("end-state %4d %s\n", + maTypeNameToIdMap.get(aAcceptingState.GetFullname()), + aAcceptingState.GetFullname()); + } + + + + + private void WriteNameList (final PrintStream aOut) + { + aOut.printf("\n# %d names\n", maNameToIdMap.size()); + for (final Entry<String, Integer> aEntry : maNameToIdMap.entrySet()) + { + aOut.printf("name %4d %s\n", + aEntry.getValue(), + aEntry.getKey()); + } + + aOut.printf("\n# %s states\n", maTypeNameToIdMap.size()); + for (final Entry<String, Integer> aEntry : maTypeNameToIdMap.entrySet()) + { + aOut.printf("state-name %4d %s\n", + aEntry.getValue(), + aEntry.getKey()); + } + } + + + + + private void WriteAutomatonList (final PrintStream aOut) + { + for (final FiniteAutomaton aAutomaton : maAutomatons.GetAutomatons()) + { + aOut.printf("# %s at %s\n", aAutomaton.GetTypeName(), aAutomaton.GetLocation()); + + final State aStartState = aAutomaton.GetStartState(); + final int nStartStateId = maTypeNameToIdMap.get(aStartState.GetFullname()); + + // Write start state. + aOut.printf("start-state %d %s\n", + nStartStateId, + aStartState); + + // Write accepting states. + for (final State aState : aAutomaton.GetAcceptingStates()) + { + aOut.printf("accepting-state %d %s\n", + maTypeNameToIdMap.get(aState.GetFullname()), + aState.GetFullname()); + } + + // Write text type. + final INode aTextType = aStartState.GetTextType(); + if (aTextType != null) + { + switch(aTextType.GetNodeType()) + { + case BuiltIn: + aOut.printf("text-type %d %d %s\n", + nStartStateId, + maTypeNameToIdMap.get(aTextType.GetName().GetStateName()), + aTextType.GetName().GetStateName()); + break; + case SimpleType: + aOut.printf("text-type %d %d %s\n", + nStartStateId, + maTypeNameToIdMap.get(aTextType.GetName().GetStateName()), + aTextType.GetName().GetStateName()); + break; + default: + throw new RuntimeException(); + } + } + + WriteAttributes( + aOut, + aStartState, + aAutomaton.GetAttributes()); + + // Write transitions. + for (final Transition aTransition : aAutomaton.GetTransitions()) + { + final Integer nId = maTypeNameToIdMap.get(aTransition.GetElementTypeName()); + aOut.printf("transition %4d %4d %2d %4d %4d %s %s %s %s\n", + maTypeNameToIdMap.get(aTransition.GetStartState().GetFullname()), + maTypeNameToIdMap.get(aTransition.GetEndState().GetFullname()), + maPrefixToIdMap.get(aTransition.GetElementName().GetNamespacePrefix()), + maNameToIdMap.get(aTransition.GetElementName().GetLocalPart()), + nId!=null ? nId : -1, + aTransition.GetStartState().GetFullname(), + aTransition.GetEndState().GetFullname(), + aTransition.GetElementName().GetStateName(), + aTransition.GetElementTypeName()); + } + // Write skip data. + for (final State aState : aAutomaton.GetStates()) + { + for (@SuppressWarnings("unused") final SkipData aSkipData : aState.GetSkipData()) + aOut.printf("skip %4d %s\n", + maTypeNameToIdMap.get(aState.GetFullname()), + aState.GetFullname()); + } + } + } + + + + + private void WriteAttributes ( + final PrintStream aOut, + final State aState, + final Iterable<Attribute> aAttributes) + { + // Write attributes. + for (final Attribute aAttribute : aAttributes) + { + aOut.printf("attribute %4d %2d %c %4d %4d %s %s %s %s %s\n", + maTypeNameToIdMap.get(aState.GetFullname()), + maPrefixToIdMap.get(aAttribute.GetName().GetNamespacePrefix()), + aAttribute.GetFormDefault()==FormDefault.qualified ? 'q' : 'u', + maNameToIdMap.get(aAttribute.GetName().GetLocalPart()), + maTypeNameToIdMap.get(aAttribute.GetTypeName().GetStateName()), + aAttribute.GetUse()==Use.Optional ? 'o' : 'u', + aAttribute.GetDefault()==null ? "null" : '"'+aAttribute.GetDefault()+'"', + aState.GetFullname(), + aAttribute.GetName().GetStateName(), + aAttribute.GetTypeName().GetStateName()); + } + } + + + + + private void WriteSimpleTypes ( + final PrintStream aOut) + { + if (maSimpleTypes == null) + { + aOut.printf("\n// There is no simple type information.\n"); + } + else + { + aOut.printf("\n// %d simple types.\n", maSimpleTypes.GetSimpleTypeCount()); + for (final Entry<String,SimpleTypeDescriptor> aEntry : maSimpleTypes.GetSimpleTypesSorted()) + { + int nIndex = 0; + for (final ISimpleTypeNode aSubType : aEntry.getValue().GetSubType()) + { + final int nCurrentIndex = nIndex++; + + final StringBuffer aLine = new StringBuffer(); + aLine.append(String.format( + "simple-type %5d %1d %c ", + maTypeNameToIdMap.get(aEntry.getKey()), + nCurrentIndex, + aSubType.IsList() ? 'L' : 'T')); + + aSubType.AcceptVisitor(new ISimpleTypeNodeVisitor() + { + @Override public void Visit(UnionNode aType) + { + throw new RuntimeException("unexpected"); + } + @Override public void Visit(StringNode aType) + { + AppendStringDescription(aLine, aType); + } + @Override public void Visit(NumberNode<?> aType) + { + AppendNumberDescription(aLine, aType); + } + @Override public void Visit(DateTimeNode aType) + { + AppendDateTimeDescription(aLine, aType); + } + @Override public void Visit(BlobNode aType) + { + AppendBlobDescription(aLine, aType); + } + }); + aOut.printf("%s\n", aLine.toString()); + } + } + } + } + + + + + private void WriteAttributeValues ( + final PrintStream aOut) + { + final Map<String,Integer> aSortedMap = new TreeMap<>(); + aSortedMap.putAll(maAttributeValueToIdMap); + aOut.printf("// %d attribute values from enumerations.\n", maAttributeValueToIdMap.size()); + for (final Entry<String,Integer> aEntry : aSortedMap.entrySet()) + aOut.printf("attribute-value %5d %s\n", aEntry.getValue(), QuoteString(aEntry.getKey())); + } + + + + + private static void AppendStringDescription ( + final StringBuffer aLine, + final StringNode aType) + { + aLine.append("S "); + switch(aType.GetRestrictionType()) + { + case Enumeration: + aLine.append('E'); + for (final int nValueId : aType.GetEnumerationRestriction()) + { + aLine.append(' '); + aLine.append(nValueId); + } + break; + case Pattern: + aLine.append("P "); + aLine.append(QuoteString(aType.GetPatternRestriction())); + break; + case Length: + aLine.append("L "); + final int[] aLengthRestriction = aType.GetLengthRestriction(); + aLine.append(aLengthRestriction[0]); + aLine.append(' '); + aLine.append(aLengthRestriction[1]); + break; + case None: + aLine.append('N'); + break; + default: + throw new RuntimeException(); + } + } + + + + + private static void AppendNumberDescription ( + final StringBuffer aLine, + final NumberNode<?> aType) + { + aLine.append("N "); + switch(aType.GetNumberType()) + { + case Boolean: aLine.append("u1"); break; + case Byte: aLine.append("s8"); break; + case UnsignedByte: aLine.append("u8"); break; + case Short: aLine.append("s16"); break; + case UnsignedShort: aLine.append("u16"); break; + case Int: aLine.append("s32"); break; + case UnsignedInt: aLine.append("u32"); break; + case Long: aLine.append("s64"); break; + case UnsignedLong: aLine.append("u64"); break; + case Integer: aLine.append("s*"); break; + case Float: aLine.append("f"); break; + case Double: aLine.append("d"); break; + default: + throw new RuntimeException("unsupported numerical type "+aType.GetNumberType()); + } + aLine.append(' '); + switch(aType.GetRestrictionType()) + { + case Enumeration: + aLine.append("E "); + for (final Object nValue : aType.GetEnumerationRestriction()) + { + aLine.append(" "); + aLine.append(nValue); + } + break; + case Size: + aLine.append("S"); + if (aType.GetMinimum() != null) + { + if (aType.IsMinimumInclusive()) + aLine.append(" >= "); + else + aLine.append(" > "); + aLine.append(aType.GetMinimum()); + } + if (aType.GetMaximum() != null) + { + if (aType.IsMaximumInclusive()) + aLine.append(" <= "); + else + aLine.append(" < "); + aLine.append(aType.GetMaximum()); + } + break; + case None: + aLine.append("N"); + break; + default: + throw new RuntimeException("unsupported numerical restriction "+aType.GetRestrictionType()); + } + } + + + + + private static void AppendDateTimeDescription ( + final StringBuffer aLine, + final DateTimeNode aType) + { + aLine.append("D"); + } + + + + + private static void AppendBlobDescription ( + final StringBuffer aLine, + final BlobNode aType) + { + aLine.append("B "); + switch(aType.GetBlobType()) + { + case Base64Binary: + aLine.append("B "); + break; + case HexBinary: + aLine.append ("H "); + break; + default: + throw new RuntimeException("unsupported blob type"); + } + switch(aType.GetRestrictionType()) + { + case Length: + aLine.append("L "); + aLine.append(aType.GetLengthRestriction()); + break; + case None: + aLine.append("N"); + break; + default: + throw new RuntimeException(); + } + } + + + + + private static String QuoteString(final String sText) + { + return "\"" + sText.replace("\"", """).replace(" ", "%20") + "\""; + } + + + + + private final FiniteAutomatonContainer maAutomatons; + private final SimpleTypeContainer maSimpleTypes; + private final NamespaceMap maNamespaces; + private final Map<String,Integer> maNameToIdMap; + private final Map<String,Integer> maPrefixToIdMap; + private final Map<String,Integer> maTypeNameToIdMap; + private final Map<String,Integer> maAttributeValueToIdMap; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/HtmlGenerator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/HtmlGenerator.java new file mode 100644 index 000000000000..189554766ada --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/HtmlGenerator.java @@ -0,0 +1,623 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.generator.html; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.complex.All; +import org.apache.openoffice.ooxml.schema.model.complex.Any; +import org.apache.openoffice.ooxml.schema.model.complex.Choice; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexContent; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexType; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexTypeReference; +import org.apache.openoffice.ooxml.schema.model.complex.Element; +import org.apache.openoffice.ooxml.schema.model.complex.ElementReference; +import org.apache.openoffice.ooxml.schema.model.complex.Extension; +import org.apache.openoffice.ooxml.schema.model.complex.Group; +import org.apache.openoffice.ooxml.schema.model.complex.GroupReference; +import org.apache.openoffice.ooxml.schema.model.complex.OccurrenceIndicator; +import org.apache.openoffice.ooxml.schema.model.complex.Sequence; +import org.apache.openoffice.ooxml.schema.model.schema.Schema; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; +import org.apache.openoffice.ooxml.schema.model.simple.BuiltIn; +import org.apache.openoffice.ooxml.schema.model.simple.List; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleContent; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleTypeReference; +import org.apache.openoffice.ooxml.schema.model.simple.Union; + +/** Create a single HTML page that shows information about all + * complex and simple types. + */ +public class HtmlGenerator + implements INodeVisitor +{ + public HtmlGenerator( + final SchemaBase aSchemaBase, + final Map<String, Schema> aTopLevelSchemas, + final File aOutputFile) + { + maSchemaBase = aSchemaBase; + maTopLevelSchemas = aTopLevelSchemas; + msIndentation = ""; + msSingleIndentation = " "; + msSpace = " "; + try + { + maOut = new PrintStream(new FileOutputStream(aOutputFile)); + } catch (FileNotFoundException aException) + { + aException.printStackTrace(); + throw new RuntimeException(aException); + } + } + + + + + /** Read a template HTML file, expand its $... references and write the resulting content. + */ + public void Generate () + { + CopyFile("linking-template.html", true); + maOut.close(); + } + + + + + private void CopyFile ( + final String sBasename, + final boolean bIsTemplate) + { + try + { + final BufferedReader aIn = new BufferedReader( + new InputStreamReader( + new FileInputStream( + new File( + new File("bin/org/apache/openoffice/ooxml/schema/generator/html"), + sBasename)))); + + final Pattern aReferencePattern = Pattern.compile("^(.*?)\\$([^\\$]+)\\$(.*)$"); + while (true) + { + final String sLine = aIn.readLine(); + if (sLine == null) + break; + + if (bIsTemplate) + { + final Matcher aMatcher = aReferencePattern.matcher(sLine); + if (aMatcher.matches()) + { + maOut.println(aMatcher.group(1)); + switch(aMatcher.group(2)) + { + case "CSS": + CopyFile("display.css", false); + break; + case "CODE": + CopyFile("code.js", false); + break; + case "DATA": + WriteJsonData(); + break; + } + maOut.printf("%s\n", aMatcher.group(3)); + } + else + maOut.printf("%s\n", sLine); + } + else + maOut.printf("%s\n", sLine); + } + aIn.close(); + } + catch (final Exception e) + { + e.printStackTrace(); + } + } + + + + + private void WriteJsonData () + { + maOut.printf("Data={\n"); + + WriteTopLevelNodes(maSchemaBase.ComplexTypes.GetSorted()); + WriteTopLevelNodes(maSchemaBase.SimpleTypes.GetSorted()); + WriteTopLevelNodes(maSchemaBase.Groups.GetSorted()); + WriteTopLevelNodes(maSchemaBase.AttributeGroups.GetSorted()); + + maOut.printf("}\n"); + } + + + + + private void WriteTopLevelNodes (final Iterable<? extends INode> aNodes) + { + for (final INode aNode : aNodes) + { + maOut.printf(" \"%s\" : {\n", aNode.GetName().GetDisplayName()); + + final String sSavedIndentation = msIndentation; + msIndentation += msSingleIndentation + msSingleIndentation; + aNode.AcceptVisitor(this); + msIndentation = sSavedIndentation; + + maOut.printf(" }, \n"); + } + } + + + + + @Override + public void Visit (final All aNode) + { + WritePair("type", "all"); + WriteLocation(aNode); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final Any aNode) + { + WritePair("type", "any"); + WriteLocation(aNode); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final ComplexContent aNode) + { + WritePair("type", "complex-content"); + WriteLocation(aNode); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final ComplexType aNode) + { + WritePair("type", "complex-type"); + WritePair("name", aNode.GetName().GetDisplayName()); + WriteLocation(aNode); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final ComplexTypeReference aReference) + { + WritePair("type", "complex-type-reference"); + WriteLocation(aReference); + WritePair("referenced-complex-type", aReference.GetReferencedTypeName().GetDisplayName()); + WriteAttributes(aReference); + } + + + + + @Override + public void Visit (final Choice aNode) + { + WritePair("type", "choice"); + WriteLocation(aNode); + WriteChildren(aNode); + WriteAttributes(aNode); + } + + + + + @Override + public void Visit (final Element aNode) + { + WritePair("type", "element"); + WriteLocation(aNode); + WritePair("tag", aNode.GetElementName().GetDisplayName()); + WritePair("result-type", aNode.GetTypeName().GetDisplayName()); + WriteAttributes(aNode); + } + + + + + @Override + public void Visit (final ElementReference aReference) + { + WritePair("type", "element-type-reference"); + WriteLocation(aReference); + WritePair("referenced-element", aReference.GetReferencedElementName().GetDisplayName()); + WriteAttributes(aReference); + } + + + + + @Override + public void Visit (final Extension aNode) + { + WritePair("type", "extension"); + WriteLocation(aNode); + WritePair("base-type", aNode.GetBaseTypeName().GetDisplayName()); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final Group aNode) + { + WritePair("type", "group"); + WriteLocation(aNode); + WritePair("name", aNode.GetName().GetDisplayName()); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final GroupReference aReference) + { + WritePair("type", "group-reference"); + WriteLocation(aReference); + WritePair("referenced-group", aReference.GetReferencedGroupName().GetDisplayName()); + WriteAttributes(aReference); + } + + + + + @Override + public void Visit (final OccurrenceIndicator aNode) + { + WritePair("type", "occurrence"); + WriteLocation(aNode); + WritePair("minimum", aNode.GetDisplayMinimum()); + WritePair("maximum", aNode.GetDisplayMaximum()); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final Sequence aNode) + { + WritePair("type", "sequence"); + WriteLocation(aNode); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final BuiltIn aNode) + { + WritePair("type", "builtin"); + WriteLocation(aNode); + WritePair("builtin-type", aNode.GetBuiltInType().GetQualifiedName().GetDisplayName()); + WriteAttributes(aNode); + } + + + + + @Override + public void Visit (final List aNode) + { + WritePair("type", "list"); + WriteLocation(aNode); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final Restriction aNode) + { + WritePair("type", "restriction"); + WriteLocation(aNode); + WritePair("base-type", aNode.GetBaseType().GetDisplayName()); + WriteAttributes(aNode); + + if (aNode.HasFeature(Restriction.EnumerationBit)) + WritePair("enumeration", Join(aNode.GetEnumeration(), ";")); + if (aNode.HasFeature(Restriction.PatternBit)) + WritePair("pattern", QuoteString(aNode.GetPattern())); + if (aNode.HasFeature(Restriction.MinExclusiveBit)) + WritePair("exclusive-minimum", aNode.GetMinExclusive()); + if (aNode.HasFeature(Restriction.MinInclusiveBit)) + WritePair("inclusive-minimum", aNode.GetMinInclusive()); + if (aNode.HasFeature(Restriction.MaxInclusiveBit)) + WritePair("inclusive-maximum", aNode.GetMaxInclusive()); + if (aNode.HasFeature(Restriction.MaxInclusiveBit)) + WritePair("inclusive-maximum", aNode.GetMaxInclusive()); + if (aNode.HasFeature(Restriction.LengthBit)) + WritePair("length", Integer.toString(aNode.GetLength())); + if (aNode.HasFeature(Restriction.MinLengthBit)) + WritePair("minimum-length", Integer.toString(aNode.GetMinimumLength())); + if (aNode.HasFeature(Restriction.MaxLengthBit)) + WritePair("maximum-length", Integer.toString(aNode.GetMaximumLength())); + assert(aNode.GetChildCount() == 0); + } + + + + + @Override + public void Visit (final SimpleContent aNode) + { + WritePair("type", "simple-content"); + WriteLocation(aNode); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final SimpleType aNode) + { + WritePair("type", "simple-type"); + WriteLocation(aNode); + WritePair("name", aNode.GetName().GetDisplayName()); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final SimpleTypeReference aReference) + { + WritePair("type", "simple-type-reference"); + WriteLocation(aReference); + WritePair("referenced-simple-type", aReference.GetReferencedTypeName().GetDisplayName()); + WriteAttributes(aReference); + } + + + + + @Override + public void Visit (final Union aNode) + { + WritePair("type", "union"); + WriteLocation(aNode); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final AttributeGroup aNode) + { + WritePair("type", "attribute-group"); + WriteLocation(aNode); + WritePair("name", aNode.GetName().GetDisplayName()); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final AttributeReference aReference) + { + WritePair("type", "attribute-reference"); + WriteLocation(aReference); + WritePair("referenced-attribute", aReference.GetReferencedName().GetDisplayName()); + WriteAttributes(aReference); + } + + + + + @Override + public void Visit (final Attribute aNode) + { + WritePair("type", "attribute"); + WriteLocation(aNode); + WritePair("name", aNode.GetName().GetDisplayName()); + WritePair("default-value", aNode.GetDefault()); + WritePair("value-type", aNode.GetTypeName().GetDisplayName()); + WritePair("use", aNode.GetUse().toString()); + WriteAttributes(aNode); + WriteChildren(aNode); + } + + + + + @Override + public void Visit (final AttributeGroupReference aReference) + { + WritePair("type", "attribute-group-reference"); + WriteLocation(aReference); + WritePair("referenced-attribute-group", aReference.GetReferencedName().GetDisplayName()); + WriteAttributes(aReference); + } + + + + + private void WriteChildren ( + final INode aParent) + { + if (aParent.GetChildCount() > 0) + { + maOut.printf("%s\"children\" : [\n", msIndentation); + int nIndex = 0; + for (final INode aChild : aParent.GetChildren()) + { + maOut.printf("%s%s{\n", msIndentation, msSingleIndentation, nIndex++); + + final String sSavedIndentation = msIndentation; + msIndentation += msSingleIndentation + msSingleIndentation; + aChild.AcceptVisitor(this); + msIndentation = sSavedIndentation; + + maOut.printf("%s%s},\n", msIndentation, msSingleIndentation); + } + + maOut.printf("%s]\n", msIndentation); + } + } + + + + + private void WriteAttributes ( + final INode aParent) + { + if (aParent.GetAttributeCount() > 0) + { + maOut.printf("%s\"attributes\" : [\n", msIndentation); + int nIndex = 0; + for (final INode aAttribute: aParent.GetAttributes()) + { + + maOut.printf("%s%s{\n", msIndentation, msSingleIndentation, nIndex++); + + final String sSavedIndentation = msIndentation; + msIndentation += msSingleIndentation + msSingleIndentation; + aAttribute.AcceptVisitor(this); + msIndentation = sSavedIndentation; + + maOut.printf("%s%s},\n", msIndentation, msSingleIndentation); + } + + maOut.printf("%s],\n", msIndentation); + } + } + + + + + private void WriteLocation ( + final INode aNode) + { + if (aNode.GetLocation() == null) + return; + + WritePair("location", aNode.GetLocation().toString()); + } + + + + + private void WritePair ( + final String sKey, + final String sValue) + { + maOut.printf("%s\"%s\"%s:%s\"%s\"%s\n", msIndentation, sKey, msSpace, msSpace, sValue, ","); + } + + + + + private String Join (final Collection<String> aValues, final String sSeparator) + { + final Iterator<String> aIterator = aValues.iterator(); + if ( ! aIterator.hasNext()) + return ""; + + final StringBuffer aBuffer = new StringBuffer(aIterator.next()); + while (aIterator.hasNext()) + { + aBuffer.append(sSeparator); + aBuffer.append(aIterator.next()); + } + return aBuffer.toString(); + } + + + + + private String QuoteString (final String sValue) + { + return sValue.replace("<", "<").replace(">", ">").replace("\"", """); + } + + + + + private final SchemaBase maSchemaBase; + private final Map<String, Schema> maTopLevelSchemas; + private final PrintStream maOut; + private String msIndentation; + private final String msSingleIndentation; + private final String msSpace; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/code.js b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/code.js new file mode 100644 index 000000000000..64be9682e913 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/code.js @@ -0,0 +1,442 @@ +function LoadData ()
+{
+ gTypeNames = [];
+ ComplexTypeNames = [];
+ SimpleTypeNames = [];
+ for (var sName in Data)
+ {
+ gTypeNames.push(sName);
+ switch(Data[sName].type)
+ {
+ case "complex-type":
+ ComplexTypeNames.push(sName);
+ break;
+
+ case "simple-type":
+ SimpleTypeNames.push(sName);
+ break;
+ }
+ }
+ document.getElementById("message").innerHTML = "there are " + ComplexTypeNames.length + " complex types and "
+ + SimpleTypeNames.length +" simple types";
+}
+
+
+
+
+function InitializeSearch ()
+{
+ CurrentTypeName = "";
+ TypeHistory = Array();
+ LoadData();
+ ShowType("A:ST_Overlap");
+}
+
+
+
+
+function CheckInput (aField, aEvent)
+{
+ switch(aEvent.keyCode)
+ {
+ case 38:
+ --selection_index;
+ if (selection_index < 0)
+ selection_index = matches.length-1;
+ break;
+ case 40:
+ ++selection_index;
+ if (selection_index >= matches.length)
+ selection_index = 0;
+ break;
+ default:
+ matches = GetMatches(aField.value);
+ selection_index = 0;
+ }
+
+ ShowMatches(matches, selection_index);
+}
+
+
+
+
+function GetMatches (sPattern)
+{
+ var aLcPatterns = sPattern.toLowerCase().split(/\s+/);
+
+ var nTypeCount = gTypeNames.length;
+
+ var aMatches = [];
+ for (index=0; index<nTypeCount; ++index)
+ {
+ var sTypeName = gTypeNames[index];
+ var aParts = new Array(sTypeName);
+ var sLcTypeName = sTypeName.toLowerCase();
+ var bIsMatch = true;
+ var nSearchStart = 0;
+ for (nPatternIndex=0; nPatternIndex<aLcPatterns.length && bIsMatch; ++nPatternIndex)
+ {
+ var sPattern = aLcPatterns[nPatternIndex];
+ var nMatchStart = sLcTypeName.indexOf(sPattern, nSearchStart);
+ if (nMatchStart >= 0)
+ {
+ var nMatchEnd = nMatchStart + sPattern.length;
+ aParts.push(sTypeName.substring(nSearchStart, nMatchStart));
+ aParts.push(sTypeName.substring(nMatchStart, nMatchEnd));
+ nSearchStart = nMatchEnd;
+ }
+ else
+ {
+ // Only some patterns are matched.
+ bIsMatch = false;
+ }
+ }
+ if (bIsMatch)
+ {
+ if (nMatchEnd < sTypeName.length-1)
+ aParts.push(sTypeName.substring(nMatchEnd));
+ aMatches.push(aParts);
+ }
+ }
+ return aMatches;
+}
+
+
+
+
+/** Show the matching types.
+ * As there can be a great many matching types, some sort of abbreviation is necessary.
+ * Format all matches into lines of n entries.
+ * Show the line containing the selected match and the ones before and after.
+ * Show also the number of ommited matches.
+ */
+function ShowMatches (aMatches, nSelectionIndex)
+{
+ var sText = "";
+
+ var nHalfRange = 10;
+ var nMatchesPerLine = 5;
+ var nLineCount = Math.floor((aMatches.length+nMatchesPerLine-1) / nMatchesPerLine);
+ var nLineOfSelection = Math.floor(nSelectionIndex / nMatchesPerLine);
+ var nFirstDisplayedLine = nLineOfSelection>0 ? nLineOfSelection-1 : 0;
+ var nLastDisplayedLine = nLineOfSelection<nLineCount-1 ? nLineOfSelection+1 : nLineCount-1;
+
+ for (nLineIndex=nFirstDisplayedLine; nLineIndex<=nLastDisplayedLine; ++nLineIndex)
+ {
+ var nLineStartIndex = nLineIndex * nMatchesPerLine;
+ var nLineEndIndex = nLineStartIndex + nMatchesPerLine - 1;
+ if (nLineEndIndex >= aMatches.length)
+ nLineEndIndex = aMatches.length-1;
+
+ sText += "<tr>"
+ for (nIndex=nLineStartIndex; nIndex<=nLineEndIndex; ++nIndex)
+ {
+ var aMatch = aMatches[nIndex];
+ var sTypeName = aMatch[0];
+ var sMatch = "";
+ for (nPartIndex=1; nPartIndex<aMatch.length; ++nPartIndex)
+ {
+ if ((nPartIndex%2)==0)
+ sMatch += "<span class=\"match-highlight\">"+aMatch[nPartIndex]+"</span>";
+ else
+ sMatch += aMatch[nPartIndex];
+ }
+ sText += "<td>"
+ if (nIndex == nSelectionIndex)
+ {
+ sText += " <span class=\"typelink current-match\">" + sMatch + "</span>";
+ }
+ else
+ {
+ sText += " " + GetTypeLink(sMatch, sTypeName, -1);
+ }
+ sText += "</td>"
+ }
+
+ sText += "</tr>";
+ }
+ if (nFirstDisplayedLine > 0)
+ sText = "<tr><td>["+(nFirstDisplayedLine*nMatchesPerLine)+" matches] ...</td><tr>" + sText;
+ if (nLastDisplayedLine+1 < nLineCount)
+ sText += "<tr><td>... ["+((nLineCount-nLastDisplayedLine-1)*nMatchesPerLine)+" matches]</td></tr>";
+
+ document.getElementById('matches').innerHTML = "<table>"+sText+"</table>";
+ if (aMatches.length == 0)
+ {
+ document.getElementById('match-count').innerHTML = "no match:";
+ }
+ else
+ {
+ if (aMatches.length == 1)
+ document.getElementById('match-count').innerHTML = "single match:";
+ else
+ document.getElementById('match-count').innerHTML = aMatches.length+" matches:";
+
+ ShowType(aMatches[nSelectionIndex][0]);
+ }
+}
+
+
+
+
+function GetTopLevelNodeForTypeName(sTypeName)
+{
+ if (sTypeName in Data)
+ return Data[sTypeName];
+ else
+ alert(sTypeName +" is not a known complex type, simple type, or group");
+}
+
+
+
+
+/** Show the specified type.
+ * When nHistoryIndex is not -1 then the history is shortened to that (before that) index.
+ */
+function ShowType (sTypeName, nHistoryIndex)
+{
+ if (nHistoryIndex == -1)
+ {
+ if (CurrentTypeName != "")
+ {
+ TypeHistory.push(CurrentTypeName);
+ ShowHistory();
+ }
+ }
+ else
+ {
+ TypeHistory = TypeHistory.slice(0,nHistoryIndex);
+ ShowHistory();
+ }
+ CurrentTypeName = sTypeName;
+
+ var aElement = document.getElementById('result');
+
+ // Remove the old content.
+ while(aElement.childNodes.length > 0)
+ aElement.removeChild(aElement.firstChild);
+
+ // Create the new content.
+ var list = CreateDomTreeForType(GetTopLevelNodeForTypeName(sTypeName), "ul");
+
+ // Show the new content.
+ aElement.appendChild(list);
+}
+
+
+
+
+/** Create a dom sub tree for the given OOXML type that is ready for insertion into the global DOM tree.
+ */
+function CreateDomTreeForType (aNode, sType)
+{
+ var aEntry = document.createElement(sType);
+
+ if (typeof aNode==='undefined')
+ {
+ aEntry.innerHTML = "undefined node";
+ }
+ else if (typeof aNode.type==='undefined')
+ {
+ aEntry.innerHTML = "unknown type";
+ }
+ else
+ {
+ switch(aNode.type)
+ {
+ case "attribute":
+ aEntry.innerHTML = CreateValueTable([
+ "attribute",
+ "type:", GetTypeLink(aNode["value-type"], aNode["value-type"], -1),
+ "use:", aNode.use]);
+ break;
+
+ case "attribute-reference":
+ aEntry.innerHTML = CreateReference(aNode["referenced-attribute"]);
+ break;
+
+ case "builtin":
+ aEntry.innerHTML = CreateValueTable(
+ ["builtin",
+ "name:", aNode['builtin-type']
+ ]);
+ break;
+
+ case "complex-type":
+ aEntry.innerHTML = aNode.type + " " + aNode.name;
+ break;
+
+ case "complex-type-reference":
+ aEntry.innerHTML = CreateReference(aNode["referenced-complex-type"]);
+ break;
+
+ case "group":
+ aEntry.innerHTML = aNode.type + " " + aNode.name;
+ break;
+
+ case "group-reference":
+ aEntry.innerHTML = CreateReference("group", aNode["referenced-group"]);
+ break;
+
+ case "element":
+ aEntry.innerHTML = "element <b>" + aNode["tag"] + "</b> -> " + GetTypeLink(aNode["result-type"], aNode["result-type"], -1);
+ break;
+
+ case "occurrence":
+ aEntry.innerHTML = aNode.minimum +" -> " + aNode.maximum;
+ break;
+
+ case "restriction":
+ aEntry.innerHTML = CreateRestrictionRepresentation(aNode);
+ break;
+
+ case "sequence":
+ aEntry.innerHTML = "sequence";
+ break;
+
+ case "simple-type":
+ aEntry.innerHTML = aNode.type + " " + aNode.name;
+ break;
+
+ case "simple-type-reference":
+ aEntry.innerHTML = CreateReference("simple-type", aNode["referenced-simple-type"]);
+ break;
+
+ default:
+ aEntry.innerHTML = aNode.type;
+ break;
+ }
+
+ // Add nodes for attributes.
+ var aAttributes= aNode["attributes"];
+ if ( ! (typeof aAttributes==='undefined' || aAttributes.length == 0))
+ {
+ var aAttributeList = document.createElement("ul");
+ aEntry.appendChild(aAttributeList);
+
+ for (var nIndex=0; nIndex<aAttributes.length; ++nIndex)
+ {
+ var aAttributeEntry = CreateDomTreeForType(aAttributes[nIndex], "li");
+ aAttributeList.appendChild(aAttributeEntry);
+ }
+ }
+
+ // Add nodes for children.
+ var aChildren = aNode["children"];
+ if ( ! (typeof aChildren==='undefined' || aChildren.length == 0))
+ {
+ var aChildrenList = document.createElement("ul");
+ aEntry.appendChild(aChildrenList);
+
+ for (var nIndex=0; nIndex<aChildren.length; ++nIndex)
+ {
+ var aChildrenEntry = CreateDomTreeForType(aChildren[nIndex], "li");
+ aChildrenList.appendChild(aChildrenEntry);
+ }
+ }
+ }
+ return aEntry;
+}
+
+
+
+
+function GetTypeLink (sText, sTarget, nIndex)
+{
+ return "<span class=\"typelink\" id=\""+sTarget+"\" onclick=\"ShowType('"+sTarget+"',"+nIndex+")\">"+sText+"</span>";
+}
+
+
+
+
+function CreateValueTable (aValues)
+{
+ var sResult = "<table class=\"value-table\"><tr><td>"+aValues[0]+"</td><td>"+aValues[1]+"</td><td>"+aValues[2]+"</td></tr>";
+ for (nIndex=3; nIndex<aValues.length; nIndex+=2)
+ {
+ sResult += "<tr><td></td><td>"+aValues[nIndex]+"</td><td>"+aValues[nIndex+1]+"</td></tr>";
+ }
+ sResult += "</table>";
+ return sResult;
+}
+
+
+
+
+function CreateReference (sWhat, sTypeName)
+{
+ return "reference to "+sWhat+" "+GetTypeLink(sTypeName, sTypeName, -1) + " "
+ +CreateButton(
+ sTypeName,
+ "show",
+ "ToggleTypeReferenceExpansion('"+sTypeName+"')")
+ +"<br><span id=\"expansion-"+sTypeName+"\"></span>";
+}
+
+
+
+
+function CreateButton (sId, sText, sExpandAction)
+{
+ return "<span class=\"button\" id=\"button-"+sId+"\" onClick=\""+sExpandAction+"\">"+sText+"</span>";
+}
+
+
+
+
+function ToggleTypeReferenceExpansion(sTypeName)
+{
+ var aButton = document.getElementById("button-"+sTypeName);
+ var aExpansion = document.getElementById("expansion-"+sTypeName);
+ if (aButton.innerHTML == "show")
+ {
+ aExpansion.appendChild(CreateDomTreeForType(Data[sTypeName], "span"));
+ aButton.innerHTML = "hide";
+ }
+ else
+ {
+ aExpansion.innerHTML = "";
+ aButton.innerHTML = "show";
+ }
+}
+
+
+
+
+function ShowHistory ()
+{
+ var aElement = document.getElementById('history');
+ var sContent = "History:";
+ for (nIndex=0; nIndex<TypeHistory.length; ++nIndex)
+ {
+ if (nIndex == 0)
+ sContent += " ";
+ else
+ sContent += ", ";
+ sContent += GetTypeLink(TypeHistory[nIndex], TypeHistory[nIndex], nIndex);
+ }
+ aElement.innerHTML = sContent;
+}
+
+
+
+
+function CreateRestrictionRepresentation (aNode)
+{
+ var aTableData = ["restriction", "based on:", GetTypeLink(aNode['base-type'], aNode['base-type'], -1)];
+ AddValue(aNode, "enumeration", aTableData);
+ AddValue(aNode, "pattern", aTableData);
+ AddValue(aNode, "length", aTableData);
+ return CreateValueTable(aTableData);
+}
+
+
+
+function AddValue (aMap, sKey, aTableData)
+{
+ if (sKey in aMap)
+ {
+ aTableData.push(sKey+":");
+ aTableData.push(aMap[sKey]);
+ }
+}
diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/display.css b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/display.css new file mode 100644 index 000000000000..d7d267e9ed9d --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/display.css @@ -0,0 +1,57 @@ +* {
+ font-family: Arial;
+ font-style:normal;
+ }
+.typelink {
+ cursor: pointer; cursor: hand;
+ color: #688;
+ }
+.typelink:link { color:#003; background-color:transparent; }
+.typelink:visited { color:#003; background-color:transparent; }
+.typelink:hover {
+ text-decoration: underline;
+ }
+.typelink:active { color:#003; background-color:#acc; }
+
+.value-table { display: inline-table; }
+
+
+.button {
+ box-shadow:inset 0px 1px 0px 0px #ffffff;
+ background-color:#ededed;
+ border-top-left-radius:4px;
+ border-top-right-radius:4px;
+ border-bottom-right-radius:4px;
+ border-bottom-left-radius:4px;
+ border:1px solid #dcdcdc;
+ display: inline-block;
+ color: #777777;
+ font-size: 15px;
+ font-weight: bold;
+ height: 20px;
+ line-height: 20px;
+ width: 80px;
+ text-align:center;
+ vertical-align: middle;
+ text-shadow:1px 1px 0px #ffffff;
+ cursor: pointer; cursor: hand;
+ }
+
+.button:hover {
+ background-color:#dfdfdf;
+ }
+
+.button:active {
+ position: relative;
+ top: 1px;
+ background-color:#c0c0c0;
+ }
+
+.current-match {
+ font-weight: bold;
+ background-color:#c0c0c0;
+ }
+
+.match-highlight {
+ color: #ff0000;
+ }
diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/linking-template.html b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/linking-template.html new file mode 100644 index 000000000000..c20c67c57165 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/linking-template.html @@ -0,0 +1,22 @@ +<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8" />
+ <link rel="stylesheet" type="text/css" href="C:\source\ooxml\git\main\ooxml\source\framework\SchemaParser\src\org\apache\openoffice\ooxml\schema\generator\html\display.css">
+ </link>
+ <script type="text/javascript">
+ $DATA$
+ </script>
+ <script type="text/javascript" src="C:\source\ooxml\git\main\ooxml\source\framework\SchemaParser\src\org\apache\openoffice\ooxml\schema\generator\html\code.js">
+ </script>
+ </head>
+ <body onLoad="InitializeSearch()">
+ <div id="message"></div>
+ <input onKeyUp="CheckInput(this,event)"></input>
+ <div id="history"></div>
+ <br><br><div id="match-count">Matches:</div><br>
+ <div id="matches" cols="100" rows="20" defaultValue="type text to see matching types"></div>
+ <br>
+ <div id="result">no result yet</div>
+ </body>
+</html>
diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/template.html b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/template.html new file mode 100644 index 000000000000..2c174c137717 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/generator/html/template.html @@ -0,0 +1,24 @@ +<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8" />
+ <style type="text/css">
+ $CSS$
+ </style>
+ <script type="text/javascript">
+ $DATA$
+ </script>
+ <script type="text/javascript">
+ $CODE$
+ </script>
+ </head>
+ <body onLoad="InitializeSearch()">
+ <div id="message"></div>
+ <input onKeyUp="CheckInput(this,event)"></input>
+ <br><br><div id="match-count">Matches:</div><br>
+ <div id="history"></div>
+ <div id="matches" cols="100" rows="20" defaultValue="type text to see matching types"></div>
+ <br>
+ <div id="result">no result yet</div>
+ </body>
+</html>
diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/AttributeIterator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/AttributeIterator.java new file mode 100644 index 000000000000..2160b8b26623 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/AttributeIterator.java @@ -0,0 +1,109 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.iterator; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.NodeVisitorAdapter; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +/** Iterate over all attributes of a given node. + * References to attributes and attribute groups and their references are resolved. + * + * If you want to iterate over all attributes in a node tree then add an outer NodeIterator. + */ +public class AttributeIterator + implements Iterable<Attribute> +{ + public AttributeIterator ( + final INode aNode, + final SchemaBase aSchemaBase) + { + maAttributes = new TreeSet<Attribute>(); + CollectAttributes(aNode, aSchemaBase); + } + + + + + public Iterator<Attribute> iterator () + { + return maAttributes.iterator(); + } + + + + + private void CollectAttributes ( + final INode aType, + final SchemaBase aSchemaBase) + { + final Queue<INode> aTodo = new LinkedList<>(); + for (final INode aAttribute : aType.GetAttributes()) + aTodo.add(aAttribute); + final INodeVisitor aVisitor = new NodeVisitorAdapter() + { + @Override public void Visit (final Attribute aAttribute) + { + maAttributes.add(aAttribute); + } + @Override public void Visit (final AttributeReference aAttributeReference) + { + aTodo.add(aAttributeReference.GetReferencedAttribute(aSchemaBase)); + } + @Override public void Visit (final AttributeGroup aAttributeGroup) + { + for (final INode aGroupAttribute : aAttributeGroup.GetAttributes()) + aTodo.add(aGroupAttribute); + } + @Override public void Visit (final AttributeGroupReference aAttributeGroupReference) + { + aTodo.add(aAttributeGroupReference.GetReferencedAttributeGroup(aSchemaBase)); + } + @Override public void Default (final INode aNode) + { + throw new RuntimeException("can not handle attribute of type "+aNode.GetNodeType()); + } + }; + while ( ! aTodo.isEmpty()) + { + final INode aAttribute = aTodo.poll(); + if (aAttribute != null) + aAttribute.AcceptVisitor(aVisitor); + } + } + + + + + private final Set<Attribute> maAttributes; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/AttributeNodeIterator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/AttributeNodeIterator.java new file mode 100644 index 000000000000..0a5bc410f073 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/AttributeNodeIterator.java @@ -0,0 +1,112 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.iterator; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.NodeVisitorAdapter; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +/** This iterator is very similar to the AttributeIterator but it + * a) returns the attributes as INode objects and + * b) includes referencing nodes and group nodes in the iteration and + * c) if there are multiple references to the same attribute then each + * of them is visited. + */ +public class AttributeNodeIterator + implements Iterable<INode> +{ + public AttributeNodeIterator ( + final INode aNode, + final SchemaBase aSchemaBase) + { + maAttributes = new Vector<INode>(); + CollectAttributes(aNode, aSchemaBase); + } + + + + + public Iterator<INode> iterator () + { + return maAttributes.iterator(); + } + + + + + private void CollectAttributes ( + final INode aType, + final SchemaBase aSchemaBase) + { + final Queue<INode> aTodo = new LinkedList<>(); + for (final INode aAttribute : aType.GetAttributes()) + aTodo.add(aAttribute); + final INodeVisitor aVisitor = new NodeVisitorAdapter() + { + @Override public void Visit (final Attribute aAttribute) + { + maAttributes.add(aAttribute); + } + @Override public void Visit (final AttributeReference aAttributeReference) + { + maAttributes.add(aAttributeReference); + aTodo.add(aAttributeReference.GetReferencedAttribute(aSchemaBase)); + } + @Override public void Visit (final AttributeGroup aAttributeGroup) + { + maAttributes.add(aAttributeGroup); + for (final INode aGroupAttribute : aAttributeGroup.GetAttributes()) + aTodo.add(aGroupAttribute); + } + @Override public void Visit (final AttributeGroupReference aAttributeGroupReference) + { + maAttributes.add(aAttributeGroupReference); + aTodo.add(aAttributeGroupReference.GetReferencedAttributeGroup(aSchemaBase)); + } + @Override public void Default (final INode aNode) + { + throw new RuntimeException("can not handle attribute of type "+aNode.GetNodeType()); + } + }; + while ( ! aTodo.isEmpty()) + { + final INode aAttribute = aTodo.poll(); + if (aAttribute != null) + aAttribute.AcceptVisitor(aVisitor); + } + } + + + + + private final Vector<INode> maAttributes; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/DereferencingNodeIterator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/DereferencingNodeIterator.java new file mode 100644 index 000000000000..77c8f9837c55 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/DereferencingNodeIterator.java @@ -0,0 +1,111 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.iterator; + +import java.util.Iterator; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeReference; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +/** Iterate over all nodes in a node tree. References to groups and elements + * are resolved and the referenced nodes are included in the iteration. + */ +public class DereferencingNodeIterator + implements Iterable<INode> + +{ + public DereferencingNodeIterator ( + final INode aRoot, + final SchemaBase aSchemaBase, + final boolean bIncludeReferencingNodes) + { + maRoot = aRoot; + maSchemaBase = aSchemaBase; + mbIncludeReferencingNodes = bIncludeReferencingNodes; + } + + + + + @Override + public Iterator<INode> iterator() + { + return CollectNodes(maRoot, mbIncludeReferencingNodes).iterator(); + } + + + + + private Vector<INode> CollectNodes ( + final INode aRoot, + final boolean bIncludeReferencingNodes) + { + final Vector<INode> aNodes = new Vector<>(); + AddNodes(aNodes, aRoot, bIncludeReferencingNodes); + return aNodes; + } + + + + + private void AddNodes ( + final Vector<INode> aNodes, + final INode aRoot, + final boolean bIncludeReferencingNodes) + { + for (final INode aNode : new NodeIterator(aRoot)) + { + switch(aNode.GetNodeType()) + { + case AttributeGroupReference: + case AttributeReference: + case ComplexTypeReference: + case ElementReference: + case GroupReference: + case SimpleTypeReference: + case Extension: + if (mbIncludeReferencingNodes) + aNodes.add(aNode); + for (final INode aChild : new DereferencingNodeIterator( + ((INodeReference)aNode).GetReferencedNode(maSchemaBase), + maSchemaBase, + bIncludeReferencingNodes)) + { + aNodes.add(aChild); + } + break; + + default: + aNodes.add(aNode); + } + } + } + + + + + private final INode maRoot; + private final SchemaBase maSchemaBase; + private final boolean mbIncludeReferencingNodes; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/NodeIterator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/NodeIterator.java new file mode 100644 index 000000000000..8ecb2abe068c --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/NodeIterator.java @@ -0,0 +1,68 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.iterator; + +import java.util.Iterator; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.model.base.INode; + +/** Iterate over all nodes in a node tree. + * The root node is typically a simple or complex type. + * References to elements or groups etc. are not followed. Use the + * DereferencingNodeIterator for that. + */ +public class NodeIterator + implements Iterable<INode> +{ + public NodeIterator (final INode aRoot) + { + maNodes = new Vector<>(); + AddNodes(aRoot); + } + + + + + @Override + public Iterator<INode> iterator () + { + return maNodes.iterator(); + } + + + + + /** Recursively add child nodes depth first. + */ + private void AddNodes (final INode aNode) + { + maNodes.add(aNode); + for (final INode aChild : aNode.GetChildren()) + AddNodes(aChild); + } + + + + + private Vector<INode> maNodes; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/PermutationIterator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/PermutationIterator.java new file mode 100644 index 000000000000..e71a6d4deac2 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/iterator/PermutationIterator.java @@ -0,0 +1,137 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.iterator; + + +/** Enumerate all permutations of a given array with elements of type T. + * The permutations are created in place, i.e. the array given to the constructor + * is modified as side effect of calling HasMore(). + * + * The algorithm is taken from "The Art of Computer Programming, Volume 4, + * Fasicle 2, by Donald E. Knuth" from section 7.2.1.2, Algorithm P. + */ +public class PermutationIterator<T> +{ + public PermutationIterator ( + final T[] aItems) + { + // Count the nodes. + mnItemCount = aItems.length; + + // Set up three arrays, one with the actual nodes, one for the inversions + // and one for directions. + maItems = aItems; + maInversions = new int[mnItemCount]; + maDirections = new int[mnItemCount]; + for (int nIndex=0; nIndex<mnItemCount; ++nIndex) + { + maInversions[nIndex] = 0; + maDirections[nIndex] = 1; + } + + mbHasMorePermutations = mnItemCount>0; + mbIsNextPermutationReady = true; + } + + + + + public boolean HasMore () + { + if ( ! mbIsNextPermutationReady) + ProvideNextPermutation(); + return mbHasMorePermutations; + } + + + + + public T[] Next() + { + assert(mbHasMorePermutations && mbIsNextPermutationReady); + mbIsNextPermutationReady = false; + return maItems; + } + + + + + private void ProvideNextPermutation () + { + mbIsNextPermutationReady = true; + + // Create the next permutation. + int nJ = mnItemCount; + int nS = 0; + + while (true) + { + final int nQ = maInversions[nJ-1] + maDirections[nJ-1]; + if (nQ>=0 && nQ<nJ) + { + // Exchange j-cj+s and j-q+s + final int nIndexA = nJ-maInversions[nJ - 1]+nS - 1; + final int nIndexB = nJ-nQ+nS - 1; + final T aItem = maItems[nIndexA]; + maItems[nIndexA] = maItems[nIndexB]; + maItems[nIndexB] = aItem; + + // cj=q + maInversions[nJ - 1] = nQ; + + // Next permutation is ready. + break; + } + else + { + if (nQ==nJ) + { + // Increase s. + if (nJ == 1) + { + // All permutations generated. + mbHasMorePermutations = false; + break; + } + else + { + ++nS; + } + } + + // Switch direction. + maDirections[nJ - 1] = -maDirections[nJ - 1]; + --nJ; + } + } + } + + + + + private final int mnItemCount; + private final T[] maItems; + private final int[] maInversions; + private final int[] maDirections; + private boolean mbIsNextPermutationReady; + private boolean mbHasMorePermutations; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/misc/Log.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/misc/Log.java new file mode 100644 index 000000000000..8ac464743dae --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/misc/Log.java @@ -0,0 +1,126 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.misc; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; + +/** Make output with indentation easier. + */ +public class Log +{ + public Log (final File aFile) + { + PrintStream aOut = null; + try + { + aOut = new PrintStream(new FileOutputStream(aFile)); + } + catch (FileNotFoundException e) + { + e.printStackTrace(); + } + maOut = aOut; + mbIsActive = maOut!=null; + msIndentation = ""; + } + + + + + public void AddComment ( + final String sFormat, + final Object ... aArgumentList) + { + if (mbIsActive) + { + maOut.print(msIndentation); + maOut.print("// "); + maOut.printf(sFormat, aArgumentList); + maOut.print("\n"); + } + } + + + + + public void StartBlock () + { + if (mbIsActive) + msIndentation += " "; + } + + + + + public void EndBlock () + { + if (mbIsActive) + msIndentation = msIndentation.substring(4); + } + + + + + public void printf ( + final String sFormat, final Object ... aArgumentList) + { + if (mbIsActive) + { + final String sMessage = String.format(sFormat, aArgumentList); + maOut.print(msIndentation); + maOut.print(sMessage); + } + } + + + + + public void println ( + final String sMessage) + { + if (mbIsActive) + { + maOut.print(msIndentation); + maOut.print(sMessage); + maOut.print("\n"); + } + } + + + + + public void Close() + { + if (mbIsActive) + maOut.close(); + } + + + + + private final PrintStream maOut; + private final boolean mbIsActive; + private String msIndentation; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/Attribute.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/Attribute.java new file mode 100644 index 000000000000..84f68cb77354 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/Attribute.java @@ -0,0 +1,91 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.attribute; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.parser.FormDefault; + +/* Representation of a single attribute. + */ +public class Attribute + extends AttributeBase +{ + public Attribute ( + final QualifiedName aName, + final QualifiedName aTypeName, + final String sUse, + final String sDefault, + final String sFixed, + final FormDefault eFormDefault, + final Location aLocation) + { + super(aName, sUse, sDefault, sFixed, eFormDefault, aLocation); + maTypeName = aTypeName; + } + + + + + public QualifiedName GetTypeName () + { + return maTypeName; + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.Attribute; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return String.format( + "attribute %s of type %s, %s", + GetName().GetDisplayName(), + maTypeName.GetDisplayName(), + super.toString()); + } + + + + + private final QualifiedName maTypeName; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeBase.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeBase.java new file mode 100644 index 000000000000..4fbf987a5be7 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeBase.java @@ -0,0 +1,117 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.attribute; + +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.parser.FormDefault; + +/** Base class for both Attribute and AttributeReference classes. + */ +public abstract class AttributeBase + extends Node +{ + public enum Use + { + Optional, + Required + } + + + + public AttributeBase ( + final QualifiedName aName, + final String sUse, + final String sDefault, + final String sFixed, + final FormDefault eFormDefault, + final Location aLocation) + { + super(null, aName, aLocation); + + switch(sUse) + { + case "optional": + meUse = Use.Optional; + break; + case "required": + meUse = Use.Required; + break; + default: + throw new RuntimeException("value of 'use' attribute is neither 'optional' nor 'required' but "+sUse); + } + msDefault = sDefault; + msFixed = sFixed; + meFormDefault = eFormDefault; + } + + + + + public FormDefault GetFormDefault() + { + return meFormDefault; + } + + + + + public Use GetUse () + { + return meUse; + } + + + + + public String GetDefault() + { + return msDefault; + } + + + + + @Override + public String toString () + { + String sText = "use "+meUse.toString(); + if (msDefault != null) + sText += ", default "+msDefault; + else + sText += ", no default"; + if (msFixed != null) + sText += ", fixed to "+msFixed; + else + sText += ", not fixed"; + return sText; + } + + + + + private final Use meUse; + private final String msDefault; + private final String msFixed; + private final FormDefault meFormDefault; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeGroup.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeGroup.java new file mode 100644 index 000000000000..92b7151678d4 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeGroup.java @@ -0,0 +1,65 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.attribute; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +public class AttributeGroup + extends Node +{ + public AttributeGroup ( + final QualifiedName aName, + final Location aLocation) + { + super(null, aName, aLocation); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.AttributeGroup; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + public String toString () + { + return "attribute group "+GetName().GetDisplayName(); + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeGroupReference.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeGroupReference.java new file mode 100644 index 000000000000..aa1e7470fe0e --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeGroupReference.java @@ -0,0 +1,110 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.attribute; + +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeReference; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +public class AttributeGroupReference + extends Node + implements INodeReference +{ + public AttributeGroupReference ( + final QualifiedName aReferencedElementName, + final Location aLocation) + { + super(null, null, aLocation); + maReferencedElement = aReferencedElementName; + } + + + + + @Override + public INode GetOnlyChild () + { + throw new RuntimeException("AttributeGroupReference has no children"); + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + public QualifiedName GetReferencedName () + { + return maReferencedElement; + } + + + + + public AttributeGroup GetReferencedAttributeGroup (final SchemaBase aSchemaBase) + { + return aSchemaBase.AttributeGroups.Get(maReferencedElement); + } + + + + + @Override + public INode GetReferencedNode (final SchemaBase aSchemaBase) + { + return GetReferencedAttributeGroup(aSchemaBase); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.AttributeGroupReference; + } + + + + + @Override + public String toString () + { + return "reference to attribute group "+maReferencedElement.GetDisplayName(); + } + + + + + private final QualifiedName maReferencedElement; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeReference.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeReference.java new file mode 100644 index 000000000000..a1c97d6c2c4e --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/attribute/AttributeReference.java @@ -0,0 +1,115 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.attribute; + +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeReference; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; +import org.apache.openoffice.ooxml.schema.parser.FormDefault; + +public class AttributeReference + extends AttributeBase + implements INodeReference +{ + public AttributeReference ( + final QualifiedName aReferencedName, + final String sUse, + final String sDefault, + final String sFixed, + final FormDefault eFormDefault, + final Location aLocation) + { + super(aReferencedName, sUse, sDefault, sFixed, eFormDefault, aLocation); + + maReferencedName = aReferencedName; + } + + + + + public Attribute GetReferencedAttribute (final SchemaBase aSchemaBase) + { + final Attribute aAttribute = aSchemaBase.Attributes.Get(maReferencedName); + if (aAttribute == null) + { + throw new RuntimeException( + String.format( + "can not find attribute %s, referenced at %s", + maReferencedName.GetDisplayName(), + GetLocation())); + } + return aAttribute; + } + + + + + @Override + public INode GetReferencedNode (final SchemaBase aSchemaBase) + { + return GetReferencedAttribute(aSchemaBase); + } + + + + + public QualifiedName GetReferencedName () + { + return maReferencedName; + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.AttributeReference; + } + + + + + @Override + public void AcceptVisitor(INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return "attribute reference to "+maReferencedName; + } + + + + + private final QualifiedName maReferencedName; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/INode.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/INode.java new file mode 100644 index 000000000000..df8208b9adeb --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/INode.java @@ -0,0 +1,39 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.base; + + +/** Interface of all nodes in a tree of XML schema nodes. + * It is implemented by complex types, simple types, and attributes. + */ +public interface INode +{ + NodeType GetNodeType (); + QualifiedName GetName (); + int GetChildCount(); + Iterable<INode> GetChildren(); + INode GetOnlyChild(); + int GetAttributeCount(); + Iterable<INode> GetAttributes (); + void AcceptVisitor (final INodeVisitor aVisitor); + Location GetLocation (); +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/INodeReference.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/INodeReference.java new file mode 100644 index 000000000000..e4644563be14 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/INodeReference.java @@ -0,0 +1,32 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.base; + +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +/** Interface implemented by all references to INodes. + */ +public interface INodeReference + extends INode +{ + INode GetReferencedNode (final SchemaBase aSchema); +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/INodeVisitor.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/INodeVisitor.java new file mode 100644 index 000000000000..664384dced5a --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/INodeVisitor.java @@ -0,0 +1,85 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.base; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference; +import org.apache.openoffice.ooxml.schema.model.complex.All; +import org.apache.openoffice.ooxml.schema.model.complex.Any; +import org.apache.openoffice.ooxml.schema.model.complex.Choice; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexContent; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexType; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexTypeReference; +import org.apache.openoffice.ooxml.schema.model.complex.Element; +import org.apache.openoffice.ooxml.schema.model.complex.ElementReference; +import org.apache.openoffice.ooxml.schema.model.complex.Extension; +import org.apache.openoffice.ooxml.schema.model.complex.Group; +import org.apache.openoffice.ooxml.schema.model.complex.GroupReference; +import org.apache.openoffice.ooxml.schema.model.complex.OccurrenceIndicator; +import org.apache.openoffice.ooxml.schema.model.complex.Sequence; +import org.apache.openoffice.ooxml.schema.model.simple.BuiltIn; +import org.apache.openoffice.ooxml.schema.model.simple.List; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleContent; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleTypeReference; +import org.apache.openoffice.ooxml.schema.model.simple.Union; + +/** Interface for the visitor pattern. + * Use a node visitor instead of INode/NodeType and casting INode to a derived + * class. + * See also the default implementation NodeVisitorAdapter. + */ +public interface INodeVisitor +{ + // Complex nodes. + void Visit (final All aAll); + void Visit (final Any aAny); + void Visit (final ComplexContent aComplexContent); + void Visit (final ComplexType aNode); + void Visit (final ComplexTypeReference aTypeReference); + void Visit (final Choice aChoice); + void Visit (final Element aElement); + void Visit (final ElementReference aElementReference); + void Visit (final Extension aNode); + void Visit (final Group aGroup); + void Visit (final GroupReference aGroupReference); + void Visit (final OccurrenceIndicator aOccurrenceIndicator); + void Visit (final Sequence aNode); + + // Simple nodes. + void Visit (final BuiltIn aType); + void Visit (final List aList); + void Visit (final Restriction aRestriction); + void Visit (final SimpleContent aSimpleContent); + void Visit (final SimpleType aSimpleType); + void Visit (final SimpleTypeReference aSimpleTypeReference); + void Visit (final Union aUnion); + + // Attributes. + void Visit (final AttributeGroup attributeGroup); + void Visit (final AttributeReference attributeReference); + void Visit (final Attribute attribute); + void Visit (final AttributeGroupReference attributeGroupReference); +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/Location.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/Location.java new file mode 100644 index 000000000000..8ebd8093b014 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/Location.java @@ -0,0 +1,69 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.base; + +/** The location in a file consisting of file name, line number, column number + * and byte index in the file. + */ +public class Location +{ + public Location ( + final String sFilename, + final int nLineNumber, + final int nColumnNumber, + final int nOffset) + { + msFilename = sFilename; + mnLineNumber = nLineNumber; + mnColumnNumber = nColumnNumber; + mnOffset = nOffset; + } + + + + + public Location () + { + this("<predefined>", 0,0,0); + } + + + + + @Override + public String toString () + { + return String.format("%s:%d,%d/%d", + msFilename, + mnLineNumber, + mnColumnNumber, + mnOffset); + } + + + + + private final String msFilename; + private final int mnLineNumber; + private final int mnColumnNumber; + private final int mnOffset; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/Node.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/Node.java new file mode 100644 index 000000000000..b965b0dba1cc --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/Node.java @@ -0,0 +1,172 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.base; + +import java.util.Vector; + +/** Base class for the nodes in the type hierarchies that represent complex + * types, simple types, and attributes. + */ +public abstract class Node + implements INode, Comparable<Node> +{ + abstract public NodeType GetNodeType (); + + + + protected Node ( + final Node aParent, + final QualifiedName aName, + final Location aLocation) + { + maParent = aParent; + maName = aName; + + maLocation = aLocation; + maAttributes = new Vector<>(); + maChildren = new Vector<>(); + } + + + + + @Override + public QualifiedName GetName () + { + return maName; + } + + + + + public Node GetParent () + { + return maParent; + } + + + + + /** Store the location in the schema files. This is used for debugging or the creation of documentation. + */ + public void SetLocation ( + final Location aLocation) + { + maLocation = aLocation; + } + + + + + public Location GetLocation () + { + return maLocation; + } + + + + + public void AddAttribute (final INode aAttribute) + { + maAttributes.add(aAttribute); + } + + + + + @Override + public int GetAttributeCount () + { + return maAttributes.size(); + } + + + + + @Override + public Iterable<INode> GetAttributes () + { + return maAttributes; + } + + + + public void ClearChildren () + { + maChildren.clear(); + } + + + + public void AddChild (final INode aChild) + { + maChildren.add(aChild); + } + + + + + public Iterable<INode> GetChildren () + { + return maChildren; + } + + + + + public INode GetOnlyChild () + { + assert(maChildren.size() == 1); + return maChildren.get(0); + } + + + + + public int GetChildCount () + { + return maChildren.size(); + } + + + + + @Override + public int compareTo (final Node aOther) + { + if (maName==null || aOther.maName==null) + { + throw new RuntimeException("can not compare nodes that don't have a name"); + } + else + return maName.compareTo(aOther.maName); + } + + + + + private final Node maParent; + private final QualifiedName maName; + private Location maLocation; + private final Vector<INode> maAttributes; + private final Vector<INode> maChildren; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/NodeType.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/NodeType.java new file mode 100644 index 000000000000..98649af621f2 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/NodeType.java @@ -0,0 +1,53 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.base; + +/** Type of a node in the XML schema tree. + * It basically corresponds to the class of a node. + */ +public enum NodeType +{ + All, + Any, + Attribute, + AttributeReference, + AttributeGroup, + AttributeGroupReference, + BuiltIn, + Choice, + ComplexContent, + ComplexType, + ComplexTypeReference, + Element, + ElementReference, + Extension, + Group, + GroupReference, + List, + OccurrenceIndicator, + Restriction, + Sequence, + SimpleContent, + SimpleType, + SimpleTypeReference, + Union +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/NodeVisitorAdapter.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/NodeVisitorAdapter.java new file mode 100644 index 000000000000..2096d8df95ee --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/NodeVisitorAdapter.java @@ -0,0 +1,203 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.base; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference; +import org.apache.openoffice.ooxml.schema.model.complex.All; +import org.apache.openoffice.ooxml.schema.model.complex.Any; +import org.apache.openoffice.ooxml.schema.model.complex.Choice; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexContent; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexType; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexTypeReference; +import org.apache.openoffice.ooxml.schema.model.complex.Element; +import org.apache.openoffice.ooxml.schema.model.complex.ElementReference; +import org.apache.openoffice.ooxml.schema.model.complex.Extension; +import org.apache.openoffice.ooxml.schema.model.complex.Group; +import org.apache.openoffice.ooxml.schema.model.complex.GroupReference; +import org.apache.openoffice.ooxml.schema.model.complex.OccurrenceIndicator; +import org.apache.openoffice.ooxml.schema.model.complex.Sequence; +import org.apache.openoffice.ooxml.schema.model.simple.BuiltIn; +import org.apache.openoffice.ooxml.schema.model.simple.List; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleContent; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleTypeReference; +import org.apache.openoffice.ooxml.schema.model.simple.Union; + +/** Implementation of the INodeVisitor interface. + * All methods that are not overridden are redirected to the Default(INode) + * method. + */ +public class NodeVisitorAdapter + implements INodeVisitor +{ + @Override + public void Visit (final All aNode) + { + Default(aNode); + } + + @Override + public void Visit (final Any aNode) + { + Default(aNode); + } + + @Override + public void Visit (final Choice aNode) + { + Default(aNode); + } + + @Override + public void Visit (final ComplexContent aNode) + { + Default(aNode); + } + + @Override + public void Visit (final ComplexType aNode) + { + Default(aNode); + } + + @Override + public void Visit (final ComplexTypeReference aTypeReference) + { + Default(aTypeReference); + } + + @Override + public void Visit (final Element aNode) + { + Default(aNode); + } + + @Override + public void Visit (final ElementReference aNode) + { + Default(aNode); + } + + @Override + public void Visit (final Extension aNode) + { + Default(aNode); + } + + @Override + public void Visit (final Group aNode) + { + Default(aNode); + } + + @Override + public void Visit (final GroupReference aNode) + { + Default(aNode); + } + + @Override + public void Visit (final OccurrenceIndicator aIndicator) + { + Default(aIndicator); + } + + @Override + public void Visit(Sequence aNode) + { + Default(aNode); + } + + @Override + public void Visit (final BuiltIn aType) + { + Default(aType); + } + + @Override + public void Visit (final List aList) + { + Default(aList); + } + + @Override + public void Visit (final SimpleContent aSimpleContent) + { + Default(aSimpleContent); + } + + @Override + public void Visit (final SimpleType aSimpleType) + { + Default(aSimpleType); + } + + @Override + public void Visit (final SimpleTypeReference aSimpleTypeReference) + { + Default(aSimpleTypeReference); + } + + @Override + public void Visit (final Union aUnion) + { + Default(aUnion); + } + + @Override + public void Visit (final Restriction aRestriction) + { + Default(aRestriction); + } + + @Override + public void Visit (final AttributeGroup aAttributeGroup) + { + Default(aAttributeGroup); + } + + @Override + public void Visit (final AttributeGroupReference aAttributeGroupReference) + { + Default(aAttributeGroupReference); + } + + @Override + public void Visit (final AttributeReference aAttributeReference) + { + Default(aAttributeReference); + } + + @Override + public void Visit (final Attribute aAttribute) + { + Default(aAttribute); + } + + public void Default (final INode aNode) + { + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/QualifiedName.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/QualifiedName.java new file mode 100644 index 000000000000..d56df52cb8cf --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/base/QualifiedName.java @@ -0,0 +1,149 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.base; + +import javax.xml.namespace.QName; + +/** Similar to the QName class. A qualified name that consists of the local + * part and a namespace. + * The namespace is stored both as URI and short form (prefix). + */ +public class QualifiedName + implements Comparable<QualifiedName> +{ + public QualifiedName (final QName aName) + { + msLocalPart = aName.getLocalPart(); + msNamespacePrefix = aName.getPrefix(); + msNamespaceURI = aName.getNamespaceURI(); + } + + + + + public QualifiedName ( + final String sNamespaceURI, + final String sNamespacePrefix, + final String sLocalPart) + { + msLocalPart = sLocalPart; + msNamespacePrefix = sNamespacePrefix; + msNamespaceURI = sNamespaceURI; + } + + + + + public QualifiedName (final String sLocalPart) + { + this(null, null, sLocalPart); + } + + + + + /** Return a textual representation for informal (and informative) display. + */ + public String GetDisplayName () + { + if (msNamespacePrefix == null) + return msLocalPart; + else + return msNamespacePrefix + ":" + msLocalPart; + } + + + + + public String GetStateName() + { + if (msNamespacePrefix == null) + return msLocalPart; + else + return msNamespacePrefix + "_" + msLocalPart; + } + + + + + public String GetNamespaceURI () + { + return msNamespaceURI; + } + + + + + public String GetNamespacePrefix () + { + return msNamespacePrefix; + } + + + + + public String GetLocalPart () + { + return msLocalPart; + } + + + + + /** Compare QualifiedName objects (e.g. for sorting them). + * Primary sort key is the local part. + * Secondary key is the namespace prefix. + * Missing prefixes come before existing prefixes. + */ + @Override + public int compareTo (final QualifiedName aOther) + { + final int nComparisonResult = msLocalPart.compareTo(aOther.msLocalPart); + if (nComparisonResult != 0) + return nComparisonResult; + else + if (msNamespacePrefix==null && aOther.msNamespacePrefix==null) + return 0; + else if (msNamespacePrefix!=null && aOther.msNamespacePrefix!=null) + return msNamespacePrefix.compareTo(aOther.msNamespacePrefix); + else if (msNamespacePrefix==null) + return -1; + else + return +1; + } + + + + + @Override + public String toString () + { + return GetDisplayName(); + } + + + + + private final String msLocalPart; + private final String msNamespacePrefix; + private final String msNamespaceURI; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/All.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/All.java new file mode 100644 index 000000000000..cb1de8adf2b9 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/All.java @@ -0,0 +1,96 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; + +/** Representation of the 'all' XML schema element. All its children are + * expected to occur in any order. By default each child is optional but with + * a min=1 attribute the children can be made mandatory. + */ +public class All + extends Node +{ + public All ( + final Node aParent, + final Location aLocation) + { + super(aParent, null, aLocation); + + assert(CheckParent(aParent)); + } + + + + + /** Occurrence values of an 'all' node must be + * min=(0,1) + * max=1. + */ + private static boolean CheckParent (final Node aParent) + { + if (aParent == null) + return false; + if (aParent.GetNodeType() != NodeType.OccurrenceIndicator) + // Missing occurrence parent means that min and max have the default + // values of 0 and 1, which is valid. + return true; + + final OccurrenceIndicator aIndicator = (OccurrenceIndicator)aParent; + if (aIndicator.GetMinimum() > 1) + return false; + else if (aIndicator.GetMaximum() != 1) + return false; + else + return true; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public NodeType GetNodeType() + { + return NodeType.All; + } + + + + + @Override + public String toString () + { + return "all"; + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Any.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Any.java new file mode 100644 index 000000000000..cc2573b86d46 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Any.java @@ -0,0 +1,112 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; + +/** Representation of the 'any' XML schema element. It specifies that its + * children conform to a non-standard schema. If it is unknown than the + * children are to be ignored. + */ +public class Any + extends Node +{ + public enum ProcessContents + { + lax, + skip, + strict + } + + public Any ( + final Node aParent, + final Location aLocation, + final String sProcessContents, + final String sNamespace) + { + super(aParent, null, aLocation); + meProcessContents = ProcessContents.valueOf(sProcessContents); + maNamespaces = sNamespace.split("\\s+"); + } + + + + @Override + public NodeType GetNodeType () + { + return NodeType.Any; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + public ProcessContents GetProcessContentsFlag () + { + return meProcessContents; + } + + + + + public String[] GetNamespaces () + { + return maNamespaces; + } + + + + + @Override + public String toString () + { + final StringBuffer aBuffer = new StringBuffer(); + aBuffer.append("any processContents="); + aBuffer.append(meProcessContents.toString()); + aBuffer.append(", namespaces="); + boolean bFirst = true; + for (final String sNamespace : maNamespaces) + { + if (bFirst) + bFirst = false; + else + aBuffer.append('|'); + aBuffer.append(sNamespace); + } + return aBuffer.toString(); + } + + + + + private final ProcessContents meProcessContents; + private final String[] maNamespaces; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Choice.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Choice.java new file mode 100644 index 000000000000..ab00f1237e3e --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Choice.java @@ -0,0 +1,68 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; + +/** Representation of the 'choice' XML schema element. + * With the default max value (1) only one of its children may occur. + */ +public class Choice + extends Node +{ + public Choice ( + final Node aParent, + final Location aLocation) + { + super(aParent, null, aLocation); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.Choice; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return "choice"; + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ComplexContent.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ComplexContent.java new file mode 100644 index 000000000000..0ca462ffc56d --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ComplexContent.java @@ -0,0 +1,65 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; + +public class ComplexContent + extends Node +{ + public ComplexContent ( + final Node aParent, + final Location aLocation) + { + super(aParent, null, aLocation); + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public NodeType GetNodeType() + { + return NodeType.ComplexContent; + } + + + + + @Override + public String toString () + { + return "complex content"; + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ComplexType.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ComplexType.java new file mode 100644 index 000000000000..7ea0de9404d1 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ComplexType.java @@ -0,0 +1,72 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Representation of the 'complexType' XML schema element. It specifies the + * structure of its children. + */ +public class ComplexType + extends Node +{ + public ComplexType ( + final Node aParent, + final QualifiedName aName, + final Location aLocation) + { + super(aParent, aName, aLocation); + + assert(aName!=null); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.ComplexType; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return "complex type "+GetName().GetDisplayName(); + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ComplexTypeReference.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ComplexTypeReference.java new file mode 100644 index 000000000000..28266a4a5219 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ComplexTypeReference.java @@ -0,0 +1,100 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeReference; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +public class ComplexTypeReference + extends Node + implements INodeReference +{ + ComplexTypeReference ( + final Node aParent, + final QualifiedName aReferencedTypeName, + final Location aLocation) + { + super(aParent, null, aLocation); + maReferencedTypeName = aReferencedTypeName; + } + + + + + public ComplexType GetReferencedComplexType (final SchemaBase aSchema) + { + final Node aType = aSchema.GetTypeForName(maReferencedTypeName); + if (aType == null) + throw new RuntimeException(); + else if (aType.GetNodeType() != NodeType.ComplexType) + throw new RuntimeException(); + else + return (ComplexType)aType; + } + + + + + + public QualifiedName GetReferencedTypeName () + { + return maReferencedTypeName; + } + + + + + @Override + public INode GetReferencedNode (final SchemaBase aSchema) + { + return GetReferencedComplexType(aSchema); + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.SimpleTypeReference; + } + + + + + private final QualifiedName maReferencedTypeName; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Element.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Element.java new file mode 100644 index 000000000000..27c00fe6cf8b --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Element.java @@ -0,0 +1,109 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Representation of the 'element' XML schema element. + */ +public class Element + extends Node +{ + /** Create a new element that represents a transition to aTypeName when a + * start tag of aElementName is processed. + */ + public Element ( + final Node aParent, + final QualifiedName aElementName, + final QualifiedName aTypeName, + final Location aLocation) + { + super(aParent, aElementName, aLocation); + maElementName = aElementName; + maTypeName = aTypeName; + } + + + + + public QualifiedName GetElementName () + { + return maElementName; + } + + + + + public QualifiedName GetTypeName () + { + return maTypeName; + } + + + + + @Override + public int compareTo (final Node aOther) + { + if (aOther.GetNodeType() != NodeType.Element) + throw new RuntimeException("can not compare Element object to non-Element object"); + else + return maElementName.compareTo(((Element)aOther).maElementName); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.Element; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return "element " + maElementName.GetDisplayName() +" -> " + maTypeName.GetDisplayName(); + } + + + + + private final QualifiedName maElementName; + private final QualifiedName maTypeName; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ElementReference.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ElementReference.java new file mode 100644 index 000000000000..48b2e7fa356f --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/ElementReference.java @@ -0,0 +1,106 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeReference; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +public class ElementReference + extends Element + implements INodeReference +{ + public ElementReference ( + final Node aParent, + final QualifiedName aReferencedElementName, + final Location aLocation) + { + super(aParent, null, null, aLocation); + + maReferencedElementName = aReferencedElementName; + } + + + + + public QualifiedName GetReferencedElementName () + { + return maReferencedElementName; + } + + + + + public Element GetReferencedElement (final SchemaBase aSchema) + { + final Element aElement = aSchema.TopLevelElements.Get(maReferencedElementName); + if (aElement == null) + throw new RuntimeException("can not find element "+maReferencedElementName.GetDisplayName()); + return aElement; + } + + + + + @Override + public INode GetReferencedNode (final SchemaBase aSchema) + { + return GetReferencedElement(aSchema); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.ElementReference; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return "element reference to "+maReferencedElementName.GetDisplayName(); + } + + + + + private final QualifiedName maReferencedElementName; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Extension.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Extension.java new file mode 100644 index 000000000000..749c608f6c27 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Extension.java @@ -0,0 +1,160 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeReference; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +/** Representation of the 'extension' XML schema element. + * It extends a complex base type. + */ +public class Extension + extends Node + implements INodeReference +{ + public Extension ( + final Node aParent, + final QualifiedName aBaseTypeName, + final Location aLocation) + { + super(aParent, null, aLocation); + maBaseTypeName = aBaseTypeName; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public NodeType GetNodeType() + { + return NodeType.Extension; + } + + + + + public QualifiedName GetBaseTypeName() + { + return maBaseTypeName; + } + + + + + public INode GetBaseType (final SchemaBase aSchema) + { + return aSchema.GetTypeForName(maBaseTypeName); + } + + + + + @Override + public INode GetReferencedNode (final SchemaBase aSchema) + { + return GetBaseType(aSchema); + } + + + + + public Vector<INode> GetTypeNodes (final SchemaBase aSchemaBase) + { + final Vector<INode> aNodes = new Vector<>(); + + AddNodes(aSchemaBase.GetTypeForName(maBaseTypeName), aNodes, aSchemaBase); + for (final INode aChild : GetChildren()) + AddNodes(aChild, aNodes, aSchemaBase); + + return aNodes; + } + + + + + @Override + public String toString () + { + return "extension of base type "+maBaseTypeName.GetDisplayName(); + } + + + + + private void AddNodes ( + final INode aParent, + final Vector<INode> aNodes, + final SchemaBase aSchemaBase) + { + INode aNode = aParent; + while (true) + { + switch (aNode.GetNodeType()) + { + case Extension: + aNode = ((Extension)aNode).GetBaseType(aSchemaBase); + break; + + case ComplexContent: + case SimpleContent: + case ComplexType: + switch(aNode.GetChildCount()) + { + case 0: + return; + case 1: + aNode = aNode.GetOnlyChild(); + break; + default: + throw new RuntimeException(); + } + break; + + default: + aNodes.add(aNode); + return; + } + } + } + + + + + public QualifiedName maBaseTypeName; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Group.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Group.java new file mode 100644 index 000000000000..4358c20849a5 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Group.java @@ -0,0 +1,70 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Represents the 'group' XML schema element. + * The concepts of groups are similar to macros. + */ +public class Group + extends Node +{ + public Group ( + final Node aParent, + final QualifiedName aName, + final Location aLocation) + { + super(aParent, aName, aLocation); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.Group; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return "group "+GetName().GetDisplayName(); + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/GroupReference.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/GroupReference.java new file mode 100644 index 000000000000..048308ca9940 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/GroupReference.java @@ -0,0 +1,109 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeReference; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +public class GroupReference + extends Node + implements INodeReference +{ + public GroupReference ( + final Node aParent, + final QualifiedName aReferencedGroupName, + final Location aLocation) + { + super(aParent, null, aLocation); + + maReferencedGroupName = aReferencedGroupName; + } + + + + + public QualifiedName GetReferencedGroupName () + { + return maReferencedGroupName; + } + + + + + public Group GetReferencedGroup (final SchemaBase aSchemaBase) + { + final Node aType = aSchemaBase.GetTypeForName(maReferencedGroupName); + if (aType == null) + throw new RuntimeException("there is no type named '"+maReferencedGroupName+"' in the schema"); + else if (aType.GetNodeType()!=NodeType.Group) + throw new RuntimeException("name '"+maReferencedGroupName+"' references a "+aType.GetNodeType()+" not a group"); + else + return (Group)aType; + } + + + + + @Override + public INode GetReferencedNode (final SchemaBase aSchemaBase) + { + return GetReferencedGroup(aSchemaBase); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.GroupReference; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return "group reference to "+maReferencedGroupName.GetDisplayName(); + } + + + + + private final QualifiedName maReferencedGroupName; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/OccurrenceIndicator.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/OccurrenceIndicator.java new file mode 100644 index 000000000000..dcc41bfbb7ce --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/OccurrenceIndicator.java @@ -0,0 +1,152 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; + +/** An occurrence indicator is based on a minimum and a maximum value. + * The minimum value is 0 or larger. If it is 0 then the child node is optional. + * The maximum value is at least as large as the minimum value. + * It can be 'unbounded' in which case the child node can appear any number of times. + * 'Unbounded' is internally stored as -1. + * + * An OccurrenceIndicator represents the minOccur and maxOccur attributes of + * XML schema elements. + * + * There is usually exactly one child. + */ +public class OccurrenceIndicator + extends Node +{ + public static int unbounded = -1; + + public OccurrenceIndicator ( + final Node aParent, + final String sMinimum, + final String sMaximum, + final Location aLocation) + { + super(aParent, null, aLocation); + + mnMinimum = ParseValue(sMinimum); + mnMaximum = ParseValue(sMaximum); + + assert(mnMinimum>=0); + assert(mnMaximum==unbounded || mnMaximum>=mnMinimum); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.OccurrenceIndicator; + } + + + + + /** Return a string version of the minimum value for textual display. + */ + public String GetDisplayMinimum () + { + return Integer.toString(mnMinimum); + } + + + + + /** Return a string version of the maximum value for textual display. + */ + public String GetDisplayMaximum () + { + if (mnMaximum == unbounded) + return "unbounded"; + else + return Integer.toString(mnMaximum); + } + + + + + public int GetMinimum () + { + return mnMinimum; + } + + + + + public int GetMaximum () + { + return mnMaximum; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return String.format("occurrence %s to %s", + GetDisplayMinimum(), + GetDisplayMaximum()); + } + + + + + private static int ParseValue (final String sValue) + { + if (sValue == null) + { + // Missing values default to 1. + return 1; + } + else + switch (sValue) + { + case "0" : return 0; + case "1" : return 1; + case "unbounded" : return unbounded; + default: return Integer.parseInt(sValue); + } + } + + + + private final int mnMinimum; + private final int mnMaximum; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Sequence.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Sequence.java new file mode 100644 index 000000000000..da01a8c15e28 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/complex/Sequence.java @@ -0,0 +1,73 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.complex; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Representation of a 'sequence' XML schema element. + * It defines an order on its children. + */ + public class Sequence + extends Node +{ + public Sequence ( + final Node aParent, + final QualifiedName aName, + final Location aLocation) + { + super(aParent, aName, aLocation); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.Sequence; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + if (GetName() != null) + return "sequence "+GetName().GetDisplayName(); + else + return "sequence"; + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/optimize/CopyVisitor.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/optimize/CopyVisitor.java new file mode 100644 index 000000000000..4fb84c909bf1 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/optimize/CopyVisitor.java @@ -0,0 +1,112 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.optimize; + +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.NodeVisitorAdapter; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexType; +import org.apache.openoffice.ooxml.schema.model.complex.GroupReference; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; + +/** This visitor is called to copy the used types from the source to the target + * schema base. + */ +public class CopyVisitor + extends NodeVisitorAdapter +{ + CopyVisitor ( + final SchemaBase aSourceSchemaBase, + final SchemaBase aTargetSchemaBase) + { + maSourceSchemaBase = aSourceSchemaBase; + maTargetSchemaBase = aTargetSchemaBase; + } + + + + + @Override public void Visit (final ComplexType aComplexType) + { + maTargetSchemaBase.ComplexTypes.Add(aComplexType); + } + + @Override public void Visit (final GroupReference aGroupReference) + { + maTargetSchemaBase.Groups.Add( + aGroupReference.GetReferencedGroup(maSourceSchemaBase)); + } + + @Override public void Visit (final SimpleType aSimpleType) + { + maTargetSchemaBase.SimpleTypes.Add(aSimpleType); + } + + @Override public void Visit (final AttributeReference aAttributeReference) + { + maTargetSchemaBase.Attributes.Add( + aAttributeReference.GetReferencedAttribute(maSourceSchemaBase)); + } + + @Override public void Visit (final AttributeGroupReference aAttributeGroupReference) + { + maTargetSchemaBase.AttributeGroups.Add( + aAttributeGroupReference.GetReferencedAttributeGroup(maSourceSchemaBase)); + } + + @Override public void Default (final INode aNode) + { + switch(aNode.GetNodeType()) + { + case All: + case Any: + case Attribute: + case AttributeGroup: + case BuiltIn: + case Choice: + case ComplexContent: + case Element: + case ElementReference: + case Extension: + case Group: + case List: + case OccurrenceIndicator: + case Restriction: + case Sequence: + case SimpleContent: + case SimpleTypeReference: + case Union: + break; + + default: + throw new RuntimeException("don't know how to copy "+aNode.toString()); + } + } + + + + + private final SchemaBase maSourceSchemaBase; + private final SchemaBase maTargetSchemaBase; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/optimize/RequestVisitor.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/optimize/RequestVisitor.java new file mode 100644 index 000000000000..20aa02dd1752 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/optimize/RequestVisitor.java @@ -0,0 +1,128 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.optimize; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.NodeVisitorAdapter; +import org.apache.openoffice.ooxml.schema.model.complex.Element; +import org.apache.openoffice.ooxml.schema.model.complex.Extension; +import org.apache.openoffice.ooxml.schema.model.complex.GroupReference; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; +import org.apache.openoffice.ooxml.schema.model.simple.List; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleTypeReference; + +/** A visitor that is called for all nodes of a complex or simple type to mark + * the referenced types as being used. + */ +public class RequestVisitor + extends NodeVisitorAdapter +{ + RequestVisitor ( + final SchemaBase aSourceSchema, + final SchemaOptimizer aOptimizer) + { + maSourceSchemaBase = aSourceSchema; + maSchemaOptimizer = aOptimizer; + } + + + @Override public void Visit (final Attribute aAttribute) + { + maSchemaOptimizer.RequestType(aAttribute.GetTypeName()); + } + + @Override public void Visit (final AttributeReference aAttributeReference) + { + maSchemaOptimizer.RequestType(aAttributeReference.GetReferencedName()); + } + + @Override public void Visit (final AttributeGroupReference aAttributeGroupReference) + { + maSchemaOptimizer.RequestType(aAttributeGroupReference.GetReferencedName()); + } + + @Override public void Visit (final Element aElement) + { + maSchemaOptimizer.RequestType(aElement.GetTypeName()); + } + + @Override public void Visit (final Extension aExtension) + { + maSchemaOptimizer.RequestType(aExtension.GetBaseTypeName()); + } + + @Override public void Visit (final GroupReference aReference) + { + maSchemaOptimizer.RequestType(aReference.GetReferencedGroup(maSourceSchemaBase)); + } + + @Override public void Visit (final List aList) + { + maSchemaOptimizer.RequestType(aList.GetItemType()); + } + + @Override public void Visit (final Restriction aRestriction) + { + maSchemaOptimizer.RequestType(aRestriction.GetBaseType()); + } + + @Override public void Visit (final SimpleTypeReference aReference) + { + maSchemaOptimizer.RequestType(aReference.GetReferencedSimpleType(maSourceSchemaBase)); + } + + @Override public void Default (final INode aNode) + { + switch (aNode.GetNodeType()) + { + case All: + case Any: + case AttributeGroup: + case BuiltIn: + case Choice: + case ComplexContent: + case ComplexType: + case ElementReference: + case Group: + case List: + case OccurrenceIndicator: + case Sequence: + case SimpleContent: + case SimpleType: + case Union: + break; + + default: + throw new RuntimeException( + String.format("don't know how to request %s which was defined at %s", + aNode.toString(), + aNode.GetLocation())); + } + } + + private final SchemaBase maSourceSchemaBase; + private final SchemaOptimizer maSchemaOptimizer; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/optimize/SchemaOptimizer.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/optimize/SchemaOptimizer.java new file mode 100644 index 000000000000..808f15dc1642 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/optimize/SchemaOptimizer.java @@ -0,0 +1,140 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.optimize; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; + +import org.apache.openoffice.ooxml.schema.iterator.AttributeNodeIterator; +import org.apache.openoffice.ooxml.schema.iterator.DereferencingNodeIterator; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.complex.Element; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +/** Optimize the given schema base by creating a new one and adding only those + * complex types, simple types, groups, attributes and attribute groups, that + * are really used, i.e. used by one of the top level elements or by a type or + * group that is in use. + */ +public class SchemaOptimizer +{ + public SchemaOptimizer ( + final SchemaBase aOriginalSchemaBase) + { + maOriginalSchemaBase = aOriginalSchemaBase; + maOptimizedSchemaBase = new SchemaBase(); + maTodoList = new LinkedList<>(); + maProcessedTypes = new HashSet<>(); + } + + + + + public SchemaBase Run () + { + // Seed the work list with the top-level elements. + for (final Element aElement : maOriginalSchemaBase.TopLevelElements.GetUnsorted()) + { + maOptimizedSchemaBase.TopLevelElements.Add(aElement); + RequestType(aElement.GetTypeName()); + } + + final INodeVisitor aCopyVisitor = new CopyVisitor( + maOriginalSchemaBase, + maOptimizedSchemaBase); + final INodeVisitor aRequestVisitor = new RequestVisitor( + maOriginalSchemaBase, + this); + + while ( ! maTodoList.isEmpty()) + { + final INode aNode = maTodoList.poll(); + + // Iterate over all child nodes and attributes. + for (final INode aChild : new DereferencingNodeIterator(aNode, maOriginalSchemaBase, true)) + { + aChild.AcceptVisitor(aCopyVisitor); + aChild.AcceptVisitor(aRequestVisitor); + for (final INode aAttribute : aChild.GetAttributes()) + aAttribute.AcceptVisitor(aCopyVisitor); + for (final INode aAttribute : new AttributeNodeIterator(aChild, maOriginalSchemaBase)) + aAttribute.AcceptVisitor(aRequestVisitor); + } + + // Request used namespaces. + final QualifiedName aName = aNode.GetName(); + if (aName != null) + maOptimizedSchemaBase.Namespaces.ProvideNamespace(aName.GetNamespaceURI(), aName.GetNamespacePrefix()); + } + + /* + System.out.printf("%d original attributes\n", maOriginalSchemaBase.Attributes.GetCount()); + for (final Attribute aAttribute : maOriginalSchemaBase.Attributes.GetUnsorted()) + System.out.printf("%s\n", aAttribute); + System.out.printf("%d optimized attributes\n", maOptimizedSchemaBase.Attributes.GetCount()); + for (final Attribute aAttribute : maOptimizedSchemaBase.Attributes.GetUnsorted()) + System.out.printf("%s\n", aAttribute); + */ + + return maOptimizedSchemaBase; + } + + + + + void RequestType (final QualifiedName aName) + { + final Node aNode = maOriginalSchemaBase.GetTypeForName(aName); + if (aNode == null) + throw new RuntimeException("there is no type named '"+aName+"' in the schema"); + else + RequestType(aNode); + } + + + + + void RequestType (final INode aNode) + { + if (aNode.GetNodeType() == NodeType.SimpleTypeReference) + System.out.println(aNode); + if ( ! maProcessedTypes.contains(aNode)) + { + maProcessedTypes.add(aNode); + maTodoList.add(aNode); + } + } + + + + + private final SchemaBase maOriginalSchemaBase; + private final SchemaBase maOptimizedSchemaBase; + private final Queue<INode> maTodoList; + private final Set<INode> maProcessedTypes; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/NamespaceMap.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/NamespaceMap.java new file mode 100644 index 000000000000..d3cfdafd8a0b --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/NamespaceMap.java @@ -0,0 +1,178 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.schema; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; + +/** Map between namespace prefixes and URIs. + * While namespace URIs can have different prefixes in different schemas, + * we will use only one prefix in the OOXML parser. This class + * provides these global prefixes. + */ +public class NamespaceMap + implements Iterable<Entry<String, String>> +{ + public NamespaceMap () + { + maURIToPrefixMap = new HashMap<>(maPredefinedURIToPrefixMap); + + // Predefine namespace prefixes. + // If possible then use the ones already in use in the schema files or documents written by MS Office, + // appended with a 06 or 12 for the ECMA-376 standards of 2006 (1st edition) or 2012 (4th edition). + maURIToPrefixMap.put("http://schemas.openxmlformats.org/drawingml/2006/main", "a06"); + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/drawingml/main", "a12"); + + maURIToPrefixMap.put("http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", "wp06"); + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/drawingml/wordprocessingDrawing", "wp12"); + + maURIToPrefixMap.put("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w06"); + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/wordprocessingml/main", "w12"); + + maURIToPrefixMap.put("http://schemas.openxmlformats.org/drawingml/2006/picture", "dpct06"); + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/drawingml/picture", "dpct12"); + + maURIToPrefixMap.put("http://schemas.openxmlformats.org/officeDocument/2006/math", "m06"); + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/officeDocument/math", "m12"); + + maURIToPrefixMap.put("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "r06"); + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/officeDocument/relationships", "r12"); + + // Invent prefixes that are not in use. + maURIToPrefixMap.put("http://schemas.openxmlformats.org/spreadsheetml/2006/main", "s06"); + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/spreadsheetml/main", "s12"); + + maURIToPrefixMap.put("http://schemas.openxmlformats.org/presentationml/2006/main", "p06"); + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/presentationml/main", "p12"); + + maURIToPrefixMap.put("http://schemas.openxmlformats.org/schemaLibrary/2006/main", "sl06"); + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/schemaLibrary/main", "sl12"); + + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/drawingml/diagram", "dd12"); + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/drawingml/chart", "dc12"); + maURIToPrefixMap.put("http://purl.oclc.org/ooxml/drawingml/lockedCanvas", "dlc12"); + } + + + + + public void ProvideNamespace ( + final String sNamespaceURI, + final String sDefaultPrefix) + { + if ( ! maURIToPrefixMap.containsKey(sNamespaceURI)) + { + final String sPrefix; + // Check if we can use the given prefix. + if (sDefaultPrefix==null || IsPrefixUsed(sDefaultPrefix)) + { + // Prefix is already used. We have to create a new and unique one. + String sCandidate = null; + for (int nIndex=0; nIndex<=26; ++nIndex) + { + if (nIndex == 26) + throw new RuntimeException("can not invent more than 26 namespace names"); + sCandidate= new String(new byte[]{(byte)('A'+nIndex)}); + if ( ! maURIToPrefixMap.containsKey(sCandidate)) + break; + } + sPrefix = sCandidate; + } + else + { + // Use the given prefix. + sPrefix = sDefaultPrefix; + } + + maURIToPrefixMap.put(sNamespaceURI, sPrefix); + } + } + + + + + public String GetNamespacePrefix (final String sURI) + { + return maURIToPrefixMap.get(sURI); + } + + + + + public Iterator<Entry<String,String>> iterator () + { + return maURIToPrefixMap.entrySet().iterator(); + } + + + + + public Object GetCount() + { + return maURIToPrefixMap.size(); + } + + + + + /** Return all namespace entries sorted on the prefix. + */ + public Iterable<Entry<String,String>> GetSorted () + { + final Map<String,Entry<String,String>> aSortedEntries = new TreeMap<>(); + for (final Entry<String,String> aEntry : maURIToPrefixMap.entrySet()) + if (aEntry.getValue() == null) + aSortedEntries.put("", aEntry); + else + aSortedEntries.put(aEntry.getValue(), aEntry); + return aSortedEntries.values(); + } + + + + + private boolean IsPrefixUsed (final String sPrefix) + { + for (final String sUsedPrefix : maURIToPrefixMap.values()) + { + if (sUsedPrefix == null) + continue; + if (sUsedPrefix.equals(sPrefix)) + return true; + } + return false; + } + + + + + private final Map<String,String> maURIToPrefixMap; + private final static Map<String,String> maPredefinedURIToPrefixMap; + static + { + maPredefinedURIToPrefixMap = new HashMap<>(); + maPredefinedURIToPrefixMap.put("http://www.w3.org/2001/XMLSchema", "xsd"); + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/Schema.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/Schema.java new file mode 100644 index 000000000000..47c511ef4e12 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/Schema.java @@ -0,0 +1,107 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.schema; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexType; +import org.apache.openoffice.ooxml.schema.model.complex.Element; +import org.apache.openoffice.ooxml.schema.model.complex.Group; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; + +/** Front end of a shared SchemaBase object of a single master schema file (such + * as wml.xsd). + * Most important member is the TopLevelElements list of top level elements, + * which are unique to each master schema. + */ +public class Schema +{ + public Schema ( + final String sShortName, + final SchemaBase aBase) + { + msShortName = sShortName; + TopLevelElements = new TypeContainer<>(); + + Namespaces = aBase.Namespaces; + ComplexTypes = aBase.ComplexTypes; + SimpleTypes = aBase.SimpleTypes; + Groups = aBase.Groups; + AttributeGroups = aBase.AttributeGroups; + Attributes = aBase.Attributes; + } + + + + + public Node GetTypeForName (final QualifiedName aName) + { + final String sTypeName = aName.GetDisplayName(); + + if (ComplexTypes.Contains(sTypeName)) + return ComplexTypes.Get(sTypeName); + else if (SimpleTypes.Contains(sTypeName)) + return SimpleTypes.Get(sTypeName); + else if (Groups.Contains(sTypeName)) + return Groups.Get(sTypeName); + else if (TopLevelElements.Contains(sTypeName)) + return TopLevelElements.Get(sTypeName); + else + return null; + } + + + + + public String GetShortName () + { + return msShortName; + } + + + + + public Schema GetOptimizedSchema (final SchemaBase aSchemaBase) + { + final Schema aOptimizedSchema = new Schema(msShortName, aSchemaBase); + for (final Element aElement : TopLevelElements.GetUnsorted()) + { + aOptimizedSchema.TopLevelElements.Add(aElement); + } + + return aOptimizedSchema; + } + + + + + private final String msShortName; + public final TypeContainer<Element> TopLevelElements; + public final NamespaceMap Namespaces; + public final TypeContainer<ComplexType> ComplexTypes; + public final TypeContainer<SimpleType> SimpleTypes; + public final TypeContainer<Group> Groups; + public final TypeContainer<AttributeGroup> AttributeGroups; + public final TypeContainer<Attribute> Attributes; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/SchemaBase.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/SchemaBase.java new file mode 100644 index 000000000000..8b456acea7f4 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/SchemaBase.java @@ -0,0 +1,121 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.schema; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexType; +import org.apache.openoffice.ooxml.schema.model.complex.Element; +import org.apache.openoffice.ooxml.schema.model.complex.Group; +import org.apache.openoffice.ooxml.schema.model.optimize.SchemaOptimizer; +import org.apache.openoffice.ooxml.schema.model.simple.BuiltIn; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; + + +/** Container of elements, complex types, simple types, etc for a set of + * master schema files and the included and imported secondary schema files. + * + * See Schema objects for the set of top level elements that are unique to + * each master schema file. + */ +public class SchemaBase +{ + public SchemaBase () + { + Namespaces = new NamespaceMap(); + TopLevelElements = new TypeContainer<>(); + ComplexTypes = new TypeContainer<>(); + SimpleTypes = new TypeContainer<>(); + Groups = new TypeContainer<>(); + AttributeGroups = new TypeContainer<>(); + Attributes = new TypeContainer<>(); + AttributeValueToIdMap = new HashMap<>(); + + // Initialize the list of simple types with all known built ins ( + // these are implicitly defined). + for (final BuiltIn aType : BuiltIn.GetTypes()) + SimpleTypes.Add(aType); + } + + + + + public Node GetTypeForName (final QualifiedName aName) + { + final String sTypeName = aName.GetDisplayName(); + + if (ComplexTypes.Contains(sTypeName)) + return ComplexTypes.Get(sTypeName); + else if (SimpleTypes.Contains(sTypeName)) + return SimpleTypes.Get(sTypeName); + else if (Groups.Contains(sTypeName)) + return Groups.Get(sTypeName); + else if (Attributes.Contains(sTypeName)) + return Attributes.Get(sTypeName); + else if (AttributeGroups.Contains(sTypeName)) + return AttributeGroups.Get(sTypeName); + else + return null; + } + + + + + public Node GetSimpleTypeForName (final QualifiedName aName) + { + final String sTypeName = aName.GetDisplayName(); + + if (SimpleTypes.Contains(sTypeName)) + return SimpleTypes.Get(aName.GetDisplayName()); + else + return null; + } + + + + + /** Create a new schema object that contains only the used types, i.e. + * types that are reachable via element transitions, starting with the + * top level elements. + */ + public SchemaBase GetOptimizedSchema (final Iterable<Schema> aTopLevelSchemas) + { + return new SchemaOptimizer(this).Run(); + } + + + + + public final NamespaceMap Namespaces; + public final TypeContainer<Element> TopLevelElements; + public final TypeContainer<ComplexType> ComplexTypes; + public final TypeContainer<SimpleType> SimpleTypes; + public final TypeContainer<Group> Groups; + public final TypeContainer<AttributeGroup> AttributeGroups; + public final TypeContainer<Attribute> Attributes; + public final Map<String,Integer> AttributeValueToIdMap; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/TypeContainer.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/TypeContainer.java new file mode 100644 index 000000000000..1855469fbba9 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/schema/TypeContainer.java @@ -0,0 +1,100 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.schema; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +public class TypeContainer<T extends Node> +{ + TypeContainer () + { + maTypes = new HashMap<>(); + } + + + + + public void Add (final T aType) + { + maTypes.put(aType.GetName().GetDisplayName(), aType); + } + + + + + public T Get (final QualifiedName aName) + { + return maTypes.get(aName.GetDisplayName()); + } + + + + + public T Get (final String sPrefixedName) + { + return maTypes.get(sPrefixedName); + } + + + + + public boolean Contains (final String sPrefixedName) + { + return maTypes.containsKey(sPrefixedName); + } + + + + + public int GetCount () + { + return maTypes.size(); + } + + + + + public Iterable<T> GetUnsorted () + { + return maTypes.values(); + } + + + + + public Iterable<T> GetSorted () + { + final Set<T> aSortedItems = new TreeSet<T>(maTypes.values()); + return aSortedItems; + } + + + + + Map<String,T> maTypes; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/BuiltIn.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/BuiltIn.java new file mode 100644 index 000000000000..72ae7fc278a1 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/BuiltIn.java @@ -0,0 +1,103 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.simple; + +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Representation of built in types. + */ +public class BuiltIn + extends SimpleType +{ + public static Vector<BuiltIn> GetTypes () + { + final Vector<BuiltIn> aTypes = new Vector<>(); + for (final BuiltInType eType : BuiltInType.values()) + aTypes.add(new BuiltIn(eType)); + return aTypes; + } + + + + + protected BuiltIn ( + final BuiltInType eType) + { + super(null, eType.GetQualifiedName(), new Location()); + meType = eType; + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.BuiltIn; + } + + + + + public BuiltInType GetBuiltInType () + { + return meType; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return "builtin "+GetName().GetDisplayName(); + } + + + + + private final BuiltInType meType; + + + + + public static Node GetForName(QualifiedName aName) + { + // TODO Auto-generated method stub + return null; + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/BuiltInType.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/BuiltInType.java new file mode 100644 index 000000000000..4befae2ee7f7 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/BuiltInType.java @@ -0,0 +1,109 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.simple; + +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.parser.XsdNamespace; + +public enum BuiltInType +{ + AnyURI, + Base64Binary, + Boolean, + Byte, + Float, + DateTime, + Double, + HexBinary, + ID, + Int, + Integer, + Long, + NcName, + Short, + String, + Token, + UnsignedByte, + UnsignedInt, + UnsignedLong, + UnsignedShort; + + public static BuiltInType GetForName (final QualifiedName aName) + { + assert(aName.GetNamespacePrefix().equals(XsdNamespace.Prefix)); + switch(aName.GetLocalPart()) + { + case "anyURI": return AnyURI; + case "base64Binary": return Base64Binary; + case "boolean": return Boolean; + case "byte": return Byte; + case "dateTime": return DateTime; + case "double": return Double; + case "float": return Float; + case "hexBinary": return HexBinary; + case "ID": return ID; + case "int" : return Int; + case "integer": return Integer; + case "long": return Long; + case "NCName": return NcName; + case "short": return Short; + case "string": return String; + case "token": return Token; + case "unsignedByte": return UnsignedByte; + case "unsignedInt": return UnsignedInt; + case "unsignedLong": return UnsignedLong; + case "unsignedShort": return UnsignedShort; + default: throw new RuntimeException("there is no builtin type named "+aName.GetDisplayName()); + } + } + + public QualifiedName GetQualifiedName () + { + final String sTypeName; + switch(this) + { + case AnyURI: sTypeName = "anyURI"; break; + case Base64Binary: sTypeName = "base64Binary"; break; + case Boolean: sTypeName = "boolean"; break; + case Byte: sTypeName = "byte"; break; + case Double: sTypeName = "double"; break; + case DateTime: sTypeName = "dateTime"; break; + case Float: sTypeName = "float"; break; + case HexBinary: sTypeName = "hexBinary"; break; + case ID: sTypeName = "ID"; break; + case Int: sTypeName = "int"; break; + case Integer: sTypeName = "integer"; break; + case Long: sTypeName = "long"; break; + case NcName: sTypeName = "NCName"; break; + case Short: sTypeName = "short"; break; + case String: sTypeName = "string"; break; + case Token: sTypeName = "token"; break; + case UnsignedByte: sTypeName = "unsignedByte"; break; + case UnsignedInt: sTypeName = "unsignedInt"; break; + case UnsignedLong: sTypeName = "unsignedLong"; break; + case UnsignedShort: sTypeName = "unsignedShort"; break; + default: + throw new RuntimeException(); + } + return new QualifiedName(XsdNamespace.URI, XsdNamespace.Prefix, sTypeName); + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/List.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/List.java new file mode 100644 index 000000000000..cac0a43e2591 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/List.java @@ -0,0 +1,84 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.simple; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Representation of the 'list' XML schema element. + * It is similar to an array of objects that are all of the same simple type. + */ +public class List + extends Node +{ + public List ( + final Node aParent, + final QualifiedName aItemType, + final Location aLocation) + { + super(aParent, null, aLocation); + maItemType = aItemType; + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.List; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + public QualifiedName GetItemType () + { + return maItemType; + } + + + + + @Override + public String toString () + { + return "list of "+maItemType.GetDisplayName()+" items"; + } + + + + + private final QualifiedName maItemType; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/Restriction.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/Restriction.java new file mode 100644 index 000000000000..8ee71d02f0c2 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/Restriction.java @@ -0,0 +1,337 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.simple; + +import java.util.Set; +import java.util.TreeSet; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Representation of the 'restriction' XML schema element. + * It defines constraints on a another simple type. + * Examples for such restrictions are minimum and maximum values + * (inclusive or exclusive), patterns or length constraints of strings, + * sets of valid values. + */ +public class Restriction + extends Node +{ + public final static int MinInclusiveBit = 0x0001; + public final static int MinExclusiveBit = 0x0002; + public final static int MaxInclusiveBit = 0x0004; + public final static int MaxExclusiveBit = 0x0008; + public final static int LengthBit = 0x0010; + public final static int MinLengthBit = 0x0020; + public final static int MaxLengthBit = 0x0040; + public final static int PatternBit = 0x0080; + public final static int EnumerationBit = 0x0100; + + public Restriction ( + final Node aParent, + final QualifiedName aBaseType, + final Location aLocation) + { + super(aParent, null, aLocation); + maBaseType = aBaseType; + maEnumerations = new TreeSet<>(); + mnFeatures = 0; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public NodeType GetNodeType() + { + return NodeType.Restriction; + } + + + + + public QualifiedName GetBaseType () + { + return maBaseType; + } + + + + + @Override + public String toString () + { + final StringBuffer aBuffer = new StringBuffer("restriction based on "); + aBuffer.append(maBaseType.GetDisplayName()); + + if (msMinInclusive != null) + { + aBuffer.append(",minInclusive="); + aBuffer.append(msMinInclusive); + } + if (msMinExclusive != null) + { + aBuffer.append(",minExclusive="); + aBuffer.append(msMinExclusive); + } + if (msMaxInclusive != null) + { + aBuffer.append(",maxInclusive="); + aBuffer.append(msMaxInclusive); + } + if (msMaxExclusive != null) + { + aBuffer.append(",maxExclusive="); + aBuffer.append(msMaxExclusive); + } + if (HasFeature(LengthBit)) + { + aBuffer.append(",length="); + aBuffer.append(mnLength); + } + if (HasFeature(MinLengthBit)) + { + aBuffer.append(",minLength="); + aBuffer.append(mnMinLength); + } + if (HasFeature(MaxLengthBit)) + { + aBuffer.append(",maxLength="); + aBuffer.append(mnMaxLength); + } + if (msPattern != null) + { + aBuffer.append(",pattern=\""); + aBuffer.append(msPattern); + aBuffer.append('"'); + } + if ( ! maEnumerations.isEmpty()) + { + aBuffer.append(",enumerations"); + aBuffer.append(maEnumerations); + } + return aBuffer.toString(); + } + + + + public void AddEnumeration (final String sValue) + { + maEnumerations.add(sValue); + mnFeatures |= EnumerationBit; + } + + + + + public void SetMinInclusive (final String sValue) + { + msMinInclusive = sValue; + assert( ! HasFeature(MinExclusiveBit)); + mnFeatures |= MinInclusiveBit; + } + + + + + public void SetMinExclusive (final String sValue) + { + msMinExclusive = sValue; + assert( ! HasFeature(MinInclusiveBit)); + mnFeatures |= MinExclusiveBit; + } + + + + + public void SetMaxInclusive (final String sValue) + { + msMaxInclusive = sValue; + assert( ! HasFeature(MaxExclusiveBit)); + mnFeatures |= MaxInclusiveBit; + } + + + + + public void SetMaxExclusive (final String sValue) + { + msMaxExclusive = sValue; + assert( ! HasFeature(MaxInclusiveBit)); + mnFeatures |= MaxExclusiveBit; + } + + + + + public void SetLength (final String sValue) + { + mnLength = Integer.parseInt(sValue); + assert( ! HasFeature(MinLengthBit|MaxLengthBit)); + mnFeatures |= LengthBit; + } + + + + + public void SetMinLength (final String sValue) + { + mnMinLength = Integer.parseInt(sValue); + assert( ! HasFeature(LengthBit)); + mnFeatures |= MinLengthBit; + } + + + + + public void SetMaxLength (final String sValue) + { + mnMaxLength = Integer.parseInt(sValue); + assert( ! HasFeature(LengthBit)); + mnFeatures |= MaxLengthBit; + } + + + + + public void SetPattern (final String sValue) + { + msPattern = sValue; + mnFeatures |= PatternBit; + } + + + + + public String GetMinInclusive () + { + return msMinInclusive; + } + + + + + public String GetMinExclusive () + { + return msMinExclusive; + } + + + + + public String GetMaxInclusive () + { + return msMaxInclusive; + } + + + + + public String GetMaxExclusive () + { + return msMaxExclusive; + } + + + + + public Set<String> GetEnumeration() + { + return maEnumerations; + } + + + + + public int GetLength() + { + return mnLength; + } + + + + + public int GetMinimumLength() + { + return mnMinLength; + } + + + + + public int GetMaximumLength() + { + return mnMaxLength; + } + + + + + + public String GetPattern() + { + return msPattern; + } + + + + + public int GetFeatureBits () + { + return mnFeatures; + } + + + + + public boolean HasFeature (final int nBitMask) + { + return (mnFeatures & nBitMask) != 0; + } + + + + + private final QualifiedName maBaseType; + private final Set<String> maEnumerations; + private String msMinInclusive; + private String msMinExclusive; + private String msMaxInclusive; + private String msMaxExclusive; + private int mnLength; + private int mnMinLength; + private int mnMaxLength; + private String msPattern; + private int mnFeatures; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/SimpleContent.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/SimpleContent.java new file mode 100644 index 000000000000..332df9c2b99a --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/SimpleContent.java @@ -0,0 +1,65 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.simple; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; + +public class SimpleContent + extends Node +{ + public SimpleContent ( + final Node aParent, + final Location aLocation) + { + super(aParent, null, aLocation); + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public NodeType GetNodeType() + { + return NodeType.SimpleContent; + } + + + + + @Override + public String toString () + { + return "simple content"; + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/SimpleType.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/SimpleType.java new file mode 100644 index 000000000000..52366fb9c614 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/SimpleType.java @@ -0,0 +1,102 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.simple; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +/** Representation of the 'simpleType' XML schema element. + * Simple types are used for attributes or the text of elements. + */ +public class SimpleType + extends Node +{ + public SimpleType ( + final Node aParent, + final QualifiedName aName, + final Location aLocation) + { + super(aParent, aName, aLocation); + maFinalValues = null; + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.SimpleType; + } + + + + + public void SetFinal (final String[] aFinalValues) + { + maFinalValues = aFinalValues; + } + + + + + public boolean IsFinal (final String sCatgery) + { + if (maFinalValues != null) + { + for (final String sFinal : maFinalValues) + { + if (sFinal.equals(sCatgery)) + return true; + else if (sFinal.equals("#all")) + return true; + } + } + return false; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return "simple type "+GetName().GetDisplayName(); + } + + + + + private String[] maFinalValues; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/SimpleTypeReference.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/SimpleTypeReference.java new file mode 100644 index 000000000000..5925b8fd0ead --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/SimpleTypeReference.java @@ -0,0 +1,107 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.simple; + +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.INodeReference; +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; + +public class SimpleTypeReference + extends Node + implements INodeReference +{ + public SimpleTypeReference ( + final Node aParent, + final QualifiedName aReferencedTypeName, + final Location aLocation) + { + super(aParent, null, aLocation); + maReferencedTypeName = aReferencedTypeName; + } + + + + + public SimpleType GetReferencedSimpleType (final SchemaBase aSchemaBase) + { + final Node aType = aSchemaBase.GetTypeForName(maReferencedTypeName); + if (aType == null) + throw new RuntimeException("there is no type named '"+maReferencedTypeName+"' in the schema"); + else if (aType.GetNodeType()!=NodeType.SimpleType && aType.GetNodeType()!=NodeType.BuiltIn) + throw new RuntimeException("name '"+maReferencedTypeName+"' references a "+aType.GetNodeType()+" not a simple type or builtin"); + else + return (SimpleType)aType; + } + + + + + @Override + public INode GetReferencedNode (final SchemaBase aSchemaBase) + { + return GetReferencedSimpleType(aSchemaBase); + } + + + + + public QualifiedName GetReferencedTypeName() + { + return maReferencedTypeName; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.SimpleTypeReference; + } + + + + @Override + public String toString () + { + return "reference to simple type "+maReferencedTypeName; + } + + + + + private final QualifiedName maReferencedTypeName; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/Union.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/Union.java new file mode 100644 index 000000000000..78c2171c65dd --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/model/simple/Union.java @@ -0,0 +1,68 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.model.simple; + +import org.apache.openoffice.ooxml.schema.model.base.INodeVisitor; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; + +/** Representation of the 'union' XML schema element. + * Its set of valid values is the union of all of its children. + */ +public class Union + extends Node +{ + public Union ( + final Node aParent, + final Location aLocation) + { + super(aParent, null, aLocation); + } + + + + + @Override + public NodeType GetNodeType () + { + return NodeType.Union; + } + + + + + @Override + public void AcceptVisitor (final INodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + @Override + public String toString () + { + return "union"; + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/FormDefault.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/FormDefault.java new file mode 100644 index 000000000000..ac03f94d53a2 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/FormDefault.java @@ -0,0 +1,31 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.parser; + +/** Enumeration of the form default values for attributes and elements in + * XML documents. + */ +public enum FormDefault +{ + qualified, + unqualified; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/SchemaParser.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/SchemaParser.java new file mode 100644 index 000000000000..6e7bbb172691 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/SchemaParser.java @@ -0,0 +1,1211 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.parser; + +import java.io.File; +import java.io.FileInputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference; +import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.Node; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.complex.All; +import org.apache.openoffice.ooxml.schema.model.complex.Any; +import org.apache.openoffice.ooxml.schema.model.complex.Choice; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexContent; +import org.apache.openoffice.ooxml.schema.model.complex.ComplexType; +import org.apache.openoffice.ooxml.schema.model.complex.Element; +import org.apache.openoffice.ooxml.schema.model.complex.ElementReference; +import org.apache.openoffice.ooxml.schema.model.complex.Extension; +import org.apache.openoffice.ooxml.schema.model.complex.Group; +import org.apache.openoffice.ooxml.schema.model.complex.GroupReference; +import org.apache.openoffice.ooxml.schema.model.complex.OccurrenceIndicator; +import org.apache.openoffice.ooxml.schema.model.complex.Sequence; +import org.apache.openoffice.ooxml.schema.model.schema.Schema; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; +import org.apache.openoffice.ooxml.schema.model.simple.List; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleContent; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleTypeReference; +import org.apache.openoffice.ooxml.schema.model.simple.Union; + + +/** Parser for single schema file. + * Imports and includes of other schema files are stored and can be retrieved + * by calling GetImportedSchemas(). + * + * Typical usage: + * 1) Create SchemaParser for top-level schema file. + * 2) Call Parse(). + * 3) Repeat the same recursively for all imported schemas (as returned by + * GetImportedSchemas()). + * + * All top level types (complex types, simple types, elements, etc.) are + * stored in the given Schema object. + */ +public class SchemaParser +{ + public SchemaParser ( + final File aSchemaFile, + final Schema aSchema, + final SchemaBase aSchemaBase) + { + maSchema = aSchema; + maSchemaBase = aSchemaBase; + maReader = GetStreamReader(aSchemaFile); + msBasename = aSchemaFile.getName(); + maDirectory = aSchemaFile.getParentFile(); + maLocalNamespaceMap = new HashMap<>(); + maImportedSchemas = new Vector<>(); + maLastLocation = null; + } + + + + + /** Parse the schema file. + * @return + * Return false if there is any error. + * @throws XMLStreamException + */ + public void Parse () + throws XMLStreamException + { + if (maReader == null) + return; + + while (maReader.hasNext()) + { + final int nCode = maReader.next(); + switch (nCode) + { + case XMLStreamReader.START_ELEMENT: + if (maReader.getLocalName().equals("schema")) + { + ProcessSchemaTag(); + ParseSchema(); + + maLastLocation = maReader.getLocation(); + } + else + { + throw CreateErrorException("expecting top level element to be 'schema'"); + } + break; + + case XMLStreamReader.END_DOCUMENT: + return; + + default: + throw CreateErrorException("unexpected XML event %d", nCode); + } + } + } + + + + + public Iterable<File> GetImportedSchemaFilenames () + { + return maImportedSchemas; + } + + + + + public int GetLineCount () + { + if (maLastLocation != null) + return maLastLocation.getLineNumber(); + else + return 0; + } + + + + + public int GetByteCount () + { + if (maLastLocation != null) + return maLastLocation.getCharacterOffset(); + else + return 0; + } + + + + + /** Process the namespace definitions in the outer 'schema' element. + */ + private void ProcessSchemaTag () + { + GetOptionalAttributeValue("id", null); + meAttributeFormDefault = FormDefault.valueOf( + GetOptionalAttributeValue("attributeFormDefault", "unqualified")); + meElementFormDefault = FormDefault.valueOf( + GetOptionalAttributeValue("elementFormDefault", "unqualified")); + GetOptionalAttributeValue("blockDefault", null);//=(#all|list of (extension|restriction|substitution)) + GetOptionalAttributeValue("finalDefault", null);//=(#all|list of (extension|restriction|list|union)) + msTargetNamespace = GetOptionalAttributeValue("targetNamespace", null); + GetOptionalAttributeValue("version", null); + + for (int nIndex=0; nIndex<maReader.getNamespaceCount(); ++nIndex) + { + final String sPrefix = maReader.getNamespacePrefix(nIndex); + final String sURI = maReader.getNamespaceURI(nIndex); + maLocalNamespaceMap.put(sPrefix, sURI); + maSchemaBase.Namespaces.ProvideNamespace(sURI, sPrefix); + } + + maLocalNamespaceMap.put(null, msTargetNamespace); + maSchemaBase.Namespaces.ProvideNamespace(msTargetNamespace, null); + } + + + + + private void ParseSchema () + throws XMLStreamException + { + while (true) + { + switch (Next()) + { + case XMLStreamReader.START_ELEMENT: + ProcessTopLevelStartElement(); + break; + + case XMLStreamReader.END_ELEMENT: + return; + + default: + throw CreateErrorException("unexpected event (expteced START_ELEMENT): %d", maReader.getEventType()); + } + } + } + + + + + private void ProcessTopLevelStartElement () + throws XMLStreamException + { + assert(GetAttributeValue("minOccurs") == null); + assert(GetAttributeValue("maxOccurs") == null); + + switch (maReader.getLocalName()) + { + case "attribute": + maSchemaBase.Attributes.Add(ParseAttribute()); + break; + + case "attributeGroup": + maSchemaBase.AttributeGroups.Add(ParseAttributeGroup()); + break; + + case "complexType": + maSchemaBase.ComplexTypes.Add(ParseComplexType()); + break; + + case "element": + final Element aElement = ParseElement(null); + if (maSchema != null) + maSchema.TopLevelElements.Add(aElement); + maSchemaBase.TopLevelElements.Add(aElement); + break; + + case "group": + maSchemaBase.Groups.Add(ParseGroup(null)); + break; + + case "import": + ParseImport(); + break; + + case "include": + ParseInclude(); + break; + + case "simpleType": + maSchemaBase.SimpleTypes.Add(ParseSimpleType(null)); + break; + + default: + throw CreateErrorException("unexpected top level element %s", maReader.getLocalName()); + } + } + + + + + private void ProcessStartElement (final Node aParent) + throws XMLStreamException + { + final String sMinimumOccurrence = GetOptionalAttributeValue("minOccurs", "1"); + final String sMaximumOccurrence = GetOptionalAttributeValue("maxOccurs", "1"); + + final Node aLocalParent; + if ( ! (sMinimumOccurrence.equals("1") && sMaximumOccurrence.equals("1"))) + { + // Occurrence does not only have default values (min=max=1). + // Have to create an intermediate node for the occurrence indicator. + final OccurrenceIndicator aIndicator = new OccurrenceIndicator( + aParent, + sMinimumOccurrence, + sMaximumOccurrence, + GetLocation()); + aParent.AddChild(aIndicator); + aLocalParent = aIndicator; + } + else + aLocalParent = aParent; + + switch (maReader.getLocalName()) + { + case "all": + aLocalParent.AddChild(ParseAll(aLocalParent)); + break; + + case "any": + aLocalParent.AddChild(ParseAny(aLocalParent)); + break; + + case "attribute": + aLocalParent.AddAttribute(ParseAttributeOrReference()); + break; + + case "attributeGroup": + aLocalParent.AddAttribute(ParseAttributeGroupOrReference()); + break; + + case "choice": + aLocalParent.AddChild(ParseChoice(aLocalParent)); + break; + + case "complexContent": + aLocalParent.AddChild(ParseComplexContent(aLocalParent)); + break; + + case "element": + aLocalParent.AddChild(ParseElementOrReference(aLocalParent)); + break; + + case "extension": + aLocalParent.AddChild(ParseExtension(aLocalParent)); + break; + + case "group": + aLocalParent.AddChild(ParseGroupOrReference(aLocalParent)); + break; + + case "list": + aLocalParent.AddChild(ParseList(aLocalParent)); + break; + + case "sequence": + aLocalParent.AddChild(ParseSequence(aLocalParent)); + break; + + case "simpleContent": + aLocalParent.AddChild(ParseSimpleContent(aLocalParent)); + break; + + default: + throw CreateErrorException("unsupported content type %s", maReader.getLocalName()); + } + } + + + + + private void IgnoreAnnotation () + throws XMLStreamException + { + IgnoreContent(); + } + + + + + private void IgnoreContent () + throws XMLStreamException + { + while(true) + { + switch(maReader.next()) + { + case XMLStreamReader.START_ELEMENT: + IgnoreContent(); + break; + + case XMLStreamReader.END_ELEMENT: + return; + + case XMLStreamReader.COMMENT: + case XMLStreamReader.CHARACTERS: + break; + + default: + throw CreateErrorException( + "unexpected XML event %d while ignoring content", + maReader.getEventType()); + } + } + } + + + + + private All ParseAll (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("minOccurs", "maxOccurs")); + + final All aAll = new All(aParent, GetLocation()); + ParseContent(aAll); + return aAll; + } + + + + + private Any ParseAny (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("minOccurs", "maxOccurs", "namespace", "processContents")); + + final Any aAny = new Any( + aParent, + GetLocation(), + GetOptionalAttributeValue("processContents", "strict"), + GetOptionalAttributeValue("namespace", "##any")); + ExpectEndElement("ParseAny"); + return aAny; + } + + + + + private AttributeGroup ParseAttributeGroup () + throws XMLStreamException + { + assert(HasOnlyAttributes("name")); + + final AttributeGroup aGroup = new AttributeGroup(GetOptionalQualifiedName("name"), GetLocation()); + + while (true) + { + switch (Next()) + { + case XMLStreamReader.START_ELEMENT: + if ( ! maReader.getLocalName().equals("attribute")) + throw CreateErrorException( + "attributeGroup expects element 'attribute' but got %s", + maReader.getLocalName()); + else + aGroup.AddAttribute(ParseAttributeOrReference()); + break; + + case XMLStreamReader.END_ELEMENT: + return aGroup; + + default: + throw CreateErrorException( + "unexpected event when parsing attributeGroup: %d", + maReader.getEventType()); + } + } + } + + + + + private INode ParseAttributeGroupOrReference () + throws XMLStreamException + { + assert(HasOnlyAttributes("ref")); + + final INode aGroup; + if (GetAttributeValue("schemaLocation") != null) + { + aGroup = ParseAttributeGroup(); + } + else + { + aGroup = new AttributeGroupReference( + CreateQualifiedName( + GetAttributeValue("ref")), + GetLocation()); + ExpectEndElement("attribute group or reference"); + } + return aGroup; + } + + + + + private INode ParseAttributeOrReference () + throws XMLStreamException + { + final INode aAttribute; + if (GetAttributeValue("name") != null) + { + aAttribute = ParseAttribute(); + } + else + { + assert(HasOnlyAttributes("default", "fixed", "ref", "use")); + + aAttribute = new AttributeReference( + GetQualifiedName("ref"), + GetOptionalAttributeValue("use", "optional"), + GetOptionalAttributeValue("default", null), + GetOptionalAttributeValue("fixed", null), + meAttributeFormDefault, + GetLocation()); + ExpectEndElement("attribute reference"); + } + return aAttribute; + } + + + + + private Attribute ParseAttribute () + throws XMLStreamException + { + assert(HasOnlyAttributes("default", "fixed", "name", "type", "use")); + + final Attribute aAttribute = new Attribute( + GetQualifiedName("name"), + GetQualifiedName("type"), + GetOptionalAttributeValue("use", "optional"), + GetOptionalAttributeValue("default", null), + GetOptionalAttributeValue("fixed", null), + meAttributeFormDefault, + GetLocation()); + ExpectEndElement("attribute"); + + return aAttribute; + } + + + + private Choice ParseChoice (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("minOccurs", "maxOccurs")); + + final Choice aChoice = new Choice(aParent, GetLocation()); + ParseContent(aChoice); + return aChoice; + } + + + + + private ComplexContent ParseComplexContent (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("minOccurs", "maxOccurs")); + + final ComplexContent aNode = new ComplexContent(aParent, GetLocation()); + ParseContent(aNode); + return aNode; + } + + + + + private ComplexType ParseComplexType () + throws XMLStreamException + { + assert(HasOnlyAttributes("mixed", "name")); + + final ComplexType aComplexType = new ComplexType( + null, + GetQualifiedName("name"), + GetLocation()); + + ParseContent(aComplexType); + + return aComplexType; + } + + + + + private Element ParseElement (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "type")); + + final Element aElement = new Element( + aParent, + GetQualifiedName("name"), + GetQualifiedName("type"), + GetLocation()); + + ExpectEndElement("element"); + + return aElement; + } + + + + + private Element ParseElementOrReference (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "ref", "type")); + + final Element aElement; + final String sName = GetOptionalAttributeValue("name", null); + if (sName != null) + { + aElement = ParseElement(aParent); + } + else + { + final String sElementReference = GetOptionalAttributeValue("ref", null); + if (sElementReference != null) + { + ExpectEndElement("element reference"); + aElement = new ElementReference( + aParent, + CreateQualifiedName(sElementReference), + GetLocation()); + } + else + { + throw CreateErrorException("element has no name and no ref"); + } + } + return aElement; + } + + + + + private Extension ParseExtension (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("base", "minOccurs", "maxOccurs")); + + final Extension aNode = new Extension( + aParent, + CreateQualifiedName(GetAttributeValue("base")), + GetLocation()); + ParseContent(aNode); + return aNode; + } + + + + + private Group ParseGroup (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("name")); + + final Group aGroup = new Group( + aParent, + GetQualifiedName("name"), + GetLocation()); + ParseContent(aGroup); + return aGroup; + } + + + + + private Node ParseGroupOrReference (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "ref")); + + final Node aGroup; + final String sName = GetOptionalAttributeValue("name", null); + if (sName != null) + { + aGroup = ParseGroup(aParent); + } + else + { + final String sGroupReference = GetOptionalAttributeValue("ref", null); + if (sGroupReference != null) + { + ExpectEndElement("group reference"); + aGroup = new GroupReference( + aParent, + CreateQualifiedName(sGroupReference), + GetLocation()); + } + else + { + throw CreateErrorException("group has no name and no ref"); + } + } + return aGroup; + } + + + + + private Restriction ParseRestriction (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("base", "value")); + + final String sBaseType = GetAttributeValue("base"); + final Restriction aRestriction = new Restriction( + aParent, + CreateQualifiedName(sBaseType), + GetLocation()); + + while (true) + { + switch (Next()) + { + case XMLStreamReader.START_ELEMENT: + final String sValue = GetAttributeValue("value"); + switch (maReader.getLocalName()) + { + case "enumeration": + aRestriction.AddEnumeration(sValue); + break; + + case "minInclusive": + aRestriction.SetMinInclusive(sValue); + break; + + case "minExclusive": + aRestriction.SetMinExclusive(sValue); + break; + + case "maxInclusive": + aRestriction.SetMaxInclusive(sValue); + break; + + case "maxExclusive": + aRestriction.SetMaxExclusive(sValue); + break; + + case "length": + aRestriction.SetLength(sValue); + break; + + case "minLength": + aRestriction.SetMinLength(sValue); + break; + + case "maxLength": + aRestriction.SetMaxLength(sValue); + break; + + case "pattern": + aRestriction.SetPattern(sValue); + break; + + default: + throw CreateErrorException("unsupported restriction type "+maReader.getLocalName()); + } + ExpectEndElement("restriction"); + break; + + case XMLStreamReader.END_ELEMENT: + return aRestriction; + + default: + throw CreateErrorException("unexpected XML event while parsing restrictions: %d", maReader.getEventType()); + } + } + } + + + + + private List ParseList (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("itemType")); + + final List aList = new List( + aParent, + GetQualifiedName("itemType"), + GetLocation()); + ExpectEndElement("list"); + return aList; + } + + + + + private Sequence ParseSequence (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name")); + + final Sequence aSequence = new Sequence( + aParent, + GetOptionalQualifiedName("name"), + GetLocation()); + ParseContent(aSequence); + return aSequence; + } + + + + + private SimpleContent ParseSimpleContent (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("minOccurs", "maxOccurs")); + + final SimpleContent aSimpleContent = new SimpleContent( + aParent, + GetLocation()); + ParseContent(aSimpleContent); + return aSimpleContent; + } + + + + + private SimpleType ParseSimpleType (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("final", "name")); + + final SimpleType aType = new SimpleType( + aParent, + GetQualifiedName("name"), + GetLocation()); + final String sFinalValue = GetOptionalAttributeValue("final", null); + if (sFinalValue != null) + aType.SetFinal(sFinalValue.split("\\s+")); + + while (true) + { + switch (Next()) + { + case XMLStreamReader.START_ELEMENT: + switch(maReader.getLocalName()) + { + case "list": + aType.AddChild(ParseList(aType)); + break; + + case "restriction": + aType.AddChild(ParseRestriction(aType)); + break; + + case "union": + aType.AddChild(ParseUnion(aType)); + break; + + default: + throw CreateErrorException("unsupported simple type part %s", maReader.getLocalName()); + } + break; + + case XMLStreamReader.END_ELEMENT: + return aType; + + default: + throw CreateErrorException("unexpected XML event in ParseSimpleType: %s", maReader.getEventType()); + } + } + } + + + + + private Union ParseUnion (final Node aParent) + throws XMLStreamException + { + assert(HasOnlyAttributes("memberTypes")); + + final Union aUnion = new Union( + aParent, + GetLocation()); + final String[] aMemberTypes = GetAttributeValue("memberTypes").split("\\s+"); + for (int nIndex=0; nIndex<aMemberTypes.length; ++nIndex) + aUnion.AddChild( + new SimpleTypeReference( + aUnion, + CreateQualifiedName(aMemberTypes[nIndex]), + GetLocation())); + + ParseContent(aUnion); + + return aUnion; + } + + + private void ParseContent (final Node aParent) + throws XMLStreamException + { + while(true) + { + switch(Next()) + { + case XMLStreamReader.START_ELEMENT: + ProcessStartElement(aParent); + break; + + case XMLStreamReader.END_ELEMENT: + return; + + default: + throw CreateErrorException( + "unexpected XML event %d while parsing content of %s", + maReader.getEventType(), + aParent.toString()); + } + } + } + + + + + private void ParseImport () + throws XMLStreamException + { + assert(HasOnlyAttributes("id", "namespace", "schemaLocation")); + + final String sFilename = GetOptionalAttributeValue("schemaLocation", null); + if (sFilename == null) + { + final String sNamespaceName = GetAttributeValue("namespace"); + if (sNamespaceName.equals(XmlNamespace.URI)) + { + XmlNamespace.Apply(maSchemaBase); + maLocalNamespaceMap.put(XmlNamespace.Prefix, XmlNamespace.URI); + } + else + throw CreateErrorException("invalid import"); + } + else + { + maImportedSchemas.add(new File(maDirectory, sFilename)); + } + + ExpectEndElement("import"); + } + + + + + private void ParseInclude () + throws XMLStreamException + { + assert(HasOnlyAttributes("id", "schemaLocation")); + + final String sFilename = GetOptionalAttributeValue("schemaLocation", null); + if (sFilename == null) + { + throw CreateErrorException("invalid include"); + } + else + { + maImportedSchemas.add(new File(maDirectory, sFilename)); + } + + ExpectEndElement("include"); + } + + + + + private void ExpectEndElement (final String sCaller) + throws XMLStreamException + { + final int nNextEvent = Next(); + if (nNextEvent != XMLStreamReader.END_ELEMENT) + throw CreateErrorException("expected END_ELEMENT of %s but got %s", + sCaller, + nNextEvent); + } + + + + + /** Return the next relevant token from the XML stream. + * Ignores comments. + * @return + * Returns the event code of the next relevant XML event or END_DOCUMENT when the end of the file has been reached. + */ + private int Next () + throws XMLStreamException + { + while (maReader.hasNext()) + { + switch (maReader.next()) + { + case XMLStreamReader.COMMENT: + // Ignore comments. + break; + + case XMLStreamReader.CHARACTERS: + // Ignore whitespace. + if (maReader.getText().matches("^\\s+$")) + break; + else + { + // Character events are not expected in schema files + // and therefore not supported. + // Alternatively, they could easily be ignored. + throw CreateErrorException("unexpected CHARACTERS event with text [%s]", maReader.getText()); + } + + case XMLStreamReader.START_ELEMENT: + switch (maReader.getLocalName()) + { + case "annotation": + IgnoreAnnotation(); + break; + case "unique": + // Not supported. + IgnoreContent(); + break; + default: + return XMLStreamReader.START_ELEMENT; + } + break; + + default: + return maReader.getEventType(); + } + } + return XMLStreamReader.END_DOCUMENT; + } + + + + + private String GetAttributeValue (final String sAttributeLocalName) + { + return maReader.getAttributeValue(null, sAttributeLocalName); + } + + + + + private String GetOptionalAttributeValue ( + final String sAttributeLocalName, + final String sDefaultValue) + { + final String sValue = maReader.getAttributeValue(null, sAttributeLocalName); + if (sValue == null) + return sDefaultValue; + else + return sValue; + } + + + + + /** Read the specified attribute and return its value as QualifiedName object. + */ + private QualifiedName GetQualifiedName (final String sAttributeLocalName) + { + final String sName = maReader.getAttributeValue(null, sAttributeLocalName); + if (sName == null) + throw CreateErrorException( + "did not find a qualified name as value of attribute '%s' at %s:%s", + sAttributeLocalName, + msBasename, + maReader.getLocation()); + else + return CreateQualifiedName(sName); + } + + + + + private QualifiedName GetOptionalQualifiedName (final String sAttributeLocalName) + { + final String sName = maReader.getAttributeValue(null, sAttributeLocalName); + if (sName == null) + return null; + else + return CreateQualifiedName(sName); + } + + + + + /** Create a QualifiedName object from the given string. + * @param sName + * May or may not contain a namespace prefix (separated from the name + * by a colon). + */ + private QualifiedName CreateQualifiedName (final String sName) + { + final String[] aParts = sName.split(":"); + final String sNamespaceURL; + final String sLocalPart; + switch (aParts.length) + { + case 1: + // sName only consists of a local part. + // Use the target namespace as namespace. + sNamespaceURL = msTargetNamespace; + sLocalPart = aParts[0]; + break; + + case 2: + // sName is of the form prefix:local. + sNamespaceURL = maLocalNamespaceMap.get(aParts[0]); + sLocalPart = aParts[1]; + break; + + default: + throw new RuntimeException( + "the string '"+sName+"' can not be transformed into a qualified name"); + } + if (sNamespaceURL == null) + throw CreateErrorException("can not determine namespace for '%s'", sName); + // Transform the local namespace prefix into a global namespace prefix + // (different schema files can use different prefixes for the same + // namespace URI). + final String sGlobalNamespacePrefix = maSchemaBase.Namespaces.GetNamespacePrefix(sNamespaceURL); + return new QualifiedName( + sNamespaceURL, + sGlobalNamespacePrefix, + sLocalPart); + } + + + + + /** Create an XMLStreamReader for the given file. + * Returns null when there is an error. + */ + private static XMLStreamReader GetStreamReader (final File aSchemaFile) + { + final XMLInputFactory aFactory = (XMLInputFactory)XMLInputFactory.newInstance(); + aFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false); + aFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); + aFactory.setProperty(XMLInputFactory.IS_COALESCING, false); + + try + { + return aFactory.createXMLStreamReader( + aSchemaFile.getName(), + new FileInputStream(aSchemaFile)); + } + catch (Exception aException) + { + aException.printStackTrace(); + return null; + } + } + + + + + private RuntimeException CreateErrorException ( + final String sFormat, + final Object ... aArgumentList) + { + return new RuntimeException(String.format(sFormat, aArgumentList) + + String.format(" in %s at L%dC%dP%d", + msBasename, + maReader.getLocation().getLineNumber(), + maReader.getLocation().getColumnNumber(), + maReader.getLocation().getCharacterOffset())); + } + + + + + /** This predicate is only used for debugging to assert + * that no unsupported attributes are present. + * If there where then the parser has to be extended. + */ + private boolean HasOnlyAttributes (final String ... aAttributeNameList) + { + for (int nIndex=0,nCount=maReader.getAttributeCount(); nIndex<nCount; ++nIndex) + { + final String sAttributeName = maReader.getAttributeLocalName(nIndex); + boolean bFound = false; + for (final String sName : aAttributeNameList) + if (sAttributeName.equals(sName)) + { + bFound = true; + break; + } + if ( ! bFound) + { + // Attribute name was not found in the given list. + System.err.printf("attribute '%s' is not handled\n", sAttributeName); + return false; + } + } + return true; + } + + + + + private Location GetLocation () + { + final javax.xml.stream.Location aLocation = maReader.getLocation(); + return new Location( + msBasename, + aLocation.getLineNumber(), + aLocation.getColumnNumber(), + aLocation.getCharacterOffset()); + } + + + + + private final Schema maSchema; + private final SchemaBase maSchemaBase; + private final XMLStreamReader maReader; + private final String msBasename; + private final File maDirectory; + private String msTargetNamespace; + /// Map of namespace prefix to URI, local to the currently read schema file. + private final Map<String,String> maLocalNamespaceMap; + /// The names of other schema files referenced by import elements. + private final Vector<File> maImportedSchemas; + /// Some values for tracking the number of read bytes and lines. + private javax.xml.stream.Location maLastLocation; + private FormDefault meAttributeFormDefault; + private FormDefault meElementFormDefault; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/XmlNamespace.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/XmlNamespace.java new file mode 100644 index 000000000000..078c9e13de10 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/XmlNamespace.java @@ -0,0 +1,69 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.parser; + +import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; +import org.apache.openoffice.ooxml.schema.model.base.Location; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; + +/** The http://www.w3.org/XML/1998/namespace namespace is + * implicitly included in all schema files. + * + * This class makes the necessary additions to namespace map and Schema object. + */ +public class XmlNamespace +{ + public static final String URI = "http://www.w3.org/XML/1998/namespace"; + public static final String Prefix = "xml"; + + public static void Apply (final SchemaBase aSchemaBase) + { + aSchemaBase.Namespaces.ProvideNamespace(URI, Prefix); + + final QualifiedName aStSpaceSimpleTypeName = new QualifiedName(URI, Prefix, "ST__space"); + aSchemaBase.Attributes.Add( + new Attribute( + new QualifiedName(URI, Prefix, "space"), + aStSpaceSimpleTypeName, + "optional", + null, + null, + FormDefault.unqualified, + null)); + + final SimpleType aType = new SimpleType( + null, + aStSpaceSimpleTypeName, + new Location()); + final Restriction aRestriction = new Restriction( + aType, + new QualifiedName(XsdNamespace.URI, XsdNamespace.Prefix, "token"), + null); + aRestriction.AddEnumeration("default"); + aRestriction.AddEnumeration("preserve"); + aType.AddChild(aRestriction); + aSchemaBase.SimpleTypes.Add(aType); + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/XsdNamespace.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/XsdNamespace.java new file mode 100644 index 000000000000..901aca5efdc9 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/XsdNamespace.java @@ -0,0 +1,29 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.parser; + + +public class XsdNamespace +{ + public static final String URI = "http://www.w3.org/2001/XMLSchema"; + public static final String Prefix = "xsd"; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/BlobNode.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/BlobNode.java new file mode 100644 index 000000000000..ab0a4fcf44cc --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/BlobNode.java @@ -0,0 +1,131 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +import java.util.Map; + +import org.apache.openoffice.ooxml.schema.misc.Log; +import org.apache.openoffice.ooxml.schema.model.simple.BuiltInType; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; + +public class BlobNode + implements ISimpleTypeNode +{ + public BlobNode (final BuiltInType eType) + { + meType = eType; + mbIsList = false; + mnLengthRestriction = null; + } + + + + + public BuiltInType GetBlobType () + { + return meType; + } + + + + + @Override + public void ApplyRestriction ( + final Restriction aRestriction, + final Map<String,Integer> aValueToIdMap) + { + if (aRestriction.GetFeatureBits() == 0) + return; + + if (aRestriction.GetFeatureBits() != Restriction.LengthBit) + throw new RuntimeException("unsupported restriction on blob: "+aRestriction); + + mnLengthRestriction = aRestriction.GetLength(); + } + + + + + @Override + public void Print (final Log aLog) + { + aLog.printf("blob of type %s\n", meType); + } + + + + + @Override + public boolean IsList () + { + return mbIsList; + } + + + + + @Override + public void SetIsList () + { + mbIsList = true; + } + + + + + @Override + public void AcceptVisitor (final ISimpleTypeNodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + public enum RestrictionType + { + Length, + None; + } + public RestrictionType GetRestrictionType () + { + if (mnLengthRestriction != null) + return RestrictionType.Length; + else + return RestrictionType.None; + } + + + + + public int GetLengthRestriction () + { + return mnLengthRestriction; + } + + + + + private final BuiltInType meType; + private boolean mbIsList; + private Integer mnLengthRestriction; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/DateTimeNode.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/DateTimeNode.java new file mode 100644 index 000000000000..9f32145bf55c --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/DateTimeNode.java @@ -0,0 +1,92 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +import java.util.Map; + +import org.apache.openoffice.ooxml.schema.misc.Log; +import org.apache.openoffice.ooxml.schema.model.simple.BuiltInType; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; + +public class DateTimeNode + implements ISimpleTypeNode +{ + + public DateTimeNode (final BuiltInType eType) + { + mbIsList = false; + } + + + + + @Override + public void ApplyRestriction ( + final Restriction aRestriction, + final Map<String,Integer> aValueToIdMap) + { + if (aRestriction.GetFeatureBits() == 0) + return; + System.out.println(aRestriction); + } + + + + + @Override + public void Print (final Log aLog) + { + aLog.printf("date or time\n"); + } + + + + + @Override + public boolean IsList () + { + return mbIsList; + } + + + + + @Override + public void SetIsList () + { + mbIsList = true; + } + + + + + @Override + public void AcceptVisitor (final ISimpleTypeNodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + private boolean mbIsList; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/EnumerationNode.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/EnumerationNode.java new file mode 100644 index 000000000000..f4844aeaab59 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/EnumerationNode.java @@ -0,0 +1,27 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +public class EnumerationNode +{ + +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/ISimpleTypeNode.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/ISimpleTypeNode.java new file mode 100644 index 000000000000..6698b856ccc1 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/ISimpleTypeNode.java @@ -0,0 +1,48 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +import java.util.Map; + +import org.apache.openoffice.ooxml.schema.misc.Log; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; + +/** Represent a single node in the node tree of a simple type. + */ +public interface ISimpleTypeNode +{ + void ApplyRestriction ( + final Restriction aNode, + final Map<String,Integer> aValueToIdMap); + void Print (final Log aLog); + + /** List elements are not represented by their own node (type). + * There is only this flag that makes this node a list of the item type + * which is represented by the node. + */ + boolean IsList (); + + /** Set the IsList flag. + */ + void SetIsList (); + void AcceptVisitor (final ISimpleTypeNodeVisitor aVisitor); +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/ISimpleTypeNodeVisitor.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/ISimpleTypeNodeVisitor.java new file mode 100644 index 000000000000..be7ea27b0b5d --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/ISimpleTypeNodeVisitor.java @@ -0,0 +1,31 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +public interface ISimpleTypeNodeVisitor +{ + void Visit (final BlobNode aType); + void Visit (final DateTimeNode aType); + void Visit (final NumberNode<?> aType); + void Visit (final StringNode aType); + void Visit (final UnionNode aType); +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/NumberNode.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/NumberNode.java new file mode 100644 index 000000000000..6cbb320c6980 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/NumberNode.java @@ -0,0 +1,310 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +import java.util.Map; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.misc.Log; +import org.apache.openoffice.ooxml.schema.model.simple.BuiltInType; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; + +public class NumberNode<T extends Comparable<T>> + implements ISimpleTypeNode +{ + NumberNode (final BuiltInType eType) + { + meType = eType; + mbIsList = false; + } + + + + + public BuiltInType GetNumberType () + { + return meType; + } + + + + + T ParseString (final String sValue) + { + switch(meType) + { + case Float: + return (T)(Float)Float.parseFloat(sValue); + + case Double: + return (T)(Double)Double.parseDouble(sValue); + + case Byte: + return (T)(Byte)Byte.parseByte(sValue); + + case Int: + case UnsignedShort: + return (T)(Integer)Integer.parseInt(sValue); + + case Short: + case UnsignedByte: + return (T)(Short)Short.parseShort(sValue); + + case Long: + case UnsignedInt: + case Integer: + return (T)(Long)Long.parseLong(sValue); + + default: + throw new RuntimeException("unsupported type "+meType); + } + } + + + + + @Override + public void ApplyRestriction ( + final Restriction aNode, + final Map<String,Integer> aValueToIdMap) + { + if (aNode.HasFeature(Restriction.MinExclusiveBit)) + ApplyMinimum(ParseString(aNode.GetMinExclusive()), false); + if (aNode.HasFeature(Restriction.MinInclusiveBit)) + ApplyMinimum(ParseString(aNode.GetMinInclusive()), true); + + if (aNode.HasFeature(Restriction.MaxExclusiveBit)) + ApplyMaximum(ParseString(aNode.GetMaxExclusive()), false); + if (aNode.HasFeature(Restriction.MaxInclusiveBit)) + ApplyMaximum(ParseString(aNode.GetMaxInclusive()), true); + + if (aNode.HasFeature(Restriction.EnumerationBit)) + { + final Vector<T> aValues = new Vector<>(); + for (final String sEnumerationValue : aNode.GetEnumeration()) + aValues.add(ParseString(sEnumerationValue)); + ApplyEnumeration(aValues); + } + } + + + + + @Override + public void Print (final Log aLog) + { + aLog.printf("%s\n", toString()); + } + + + + + @Override + public String toString () + { + final StringBuffer sMessage = new StringBuffer(); + sMessage.append(meType); + if (maEnumeration != null) + { + sMessage.append(", restricted to values"); + for (final T nValue : maEnumeration) + { + sMessage.append(' '); + sMessage.append(nValue); + } + } + else if (maMinimumValue!=null || maMaximumValue!=null) + { + sMessage.append(" restricted to "); + + if (maMinimumValue != null) + { + sMessage.append(maMinimumValue); + if (mbIsMinimumInclusive) + sMessage.append(" <= "); + else + sMessage.append(" < "); + } + sMessage.append("value"); + if (maMaximumValue != null) + { + if (mbIsMaximumInclusive) + sMessage.append(" <= "); + else + sMessage.append(" < "); + sMessage.append(maMaximumValue); + } + } + else + sMessage.append(", not restricted"); + + return sMessage.toString(); + } + + + + + private void ApplyMinimum ( + final T nValue, + final boolean bIsInclusive) + { + if (maEnumeration != null) + throw new RuntimeException("minimum can not be applied to an enumeration"); + else if (maMinimumValue != null) + { + final int nComparison = maMinimumValue.compareTo(nValue); + if (nComparison > 0) + throw new RuntimeException("second restriction tries to enlarge value space"); + else if (nComparison == 0) + if (mbIsMinimumInclusive && ! bIsInclusive) + throw new RuntimeException("second restriction tries to enlarge value space"); + } + maMinimumValue = nValue; + mbIsMinimumInclusive = bIsInclusive; + } + + + + + + private void ApplyMaximum ( + final T nValue, + final boolean bIsInclusive) + { + if (maEnumeration != null) + throw new RuntimeException("maximum can not be applied to an enumeration"); + else if (maMaximumValue != null) + { + final int nComparison = maMaximumValue.compareTo(nValue); + if (nComparison < 0) + throw new RuntimeException("second restriction tries to enlarge value space"); + else if (nComparison == 0) + if ( ! mbIsMaximumInclusive && bIsInclusive) + throw new RuntimeException("second restriction tries to enlarge value space"); + } + maMaximumValue = nValue; + mbIsMaximumInclusive = bIsInclusive; + } + + + + + private void ApplyEnumeration (final Vector<T> aValues) + { + if (maEnumeration!=null || maMaximumValue!=null || maMinimumValue!=null) + throw new RuntimeException("can not apply enumeration to existing restriction"); + maEnumeration = aValues; + } + + + + + @Override + public boolean IsList () + { + return mbIsList; + } + + + + + @Override + public void SetIsList () + { + mbIsList = true; + } + + + + + @Override + public void AcceptVisitor (final ISimpleTypeNodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + public enum RestrictionType + { + Size, + Enumeration, + None + } + public RestrictionType GetRestrictionType () + { + if (maEnumeration != null) + return RestrictionType.Enumeration; + else if (maMinimumValue!=null || maMaximumValue!=null) + return RestrictionType.Size; + else + return RestrictionType.None; + } + + + + public Iterable<T> GetEnumerationRestriction () + { + return maEnumeration; + } + + + + + public T GetMinimum () + { + return maMinimumValue; + } + + + + + public T GetMaximum () + { + return maMaximumValue; + } + + + + public boolean IsMinimumInclusive () + { + return mbIsMinimumInclusive; + } + + + + + public boolean IsMaximumInclusive () + { + return mbIsMaximumInclusive; + } + + + + + private final BuiltInType meType; + private T maMinimumValue; + private boolean mbIsMinimumInclusive; + private T maMaximumValue; + private boolean mbIsMaximumInclusive; + private Vector<T> maEnumeration; + private boolean mbIsList; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeContainer.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeContainer.java new file mode 100644 index 000000000000..c77294c557a9 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeContainer.java @@ -0,0 +1,102 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +import java.io.File; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; + +import org.apache.openoffice.ooxml.schema.misc.Log; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; + +public class SimpleTypeContainer +{ + public static SimpleTypeContainer Create ( + final SchemaBase aSchemaBase, + final File aLogFile) + { + final SimpleTypeContainer aContainer = new SimpleTypeContainer(aLogFile); + for (final SimpleType aType : aSchemaBase.SimpleTypes.GetSorted()) + { + aContainer.ProcessSimpleType(aType, aSchemaBase); + } + return aContainer; + } + + + + + public int GetSimpleTypeCount () + { + return maSimpleTypes.size(); + } + + + + + public Iterable<Entry<String,SimpleTypeDescriptor>> GetSimpleTypes () + { + return maSimpleTypes.entrySet(); + } + + + + + public Iterable<Entry<String,SimpleTypeDescriptor>> GetSimpleTypesSorted () + { + final Map<String,SimpleTypeDescriptor> aSortedSimpleTypes = new TreeMap<>(); + aSortedSimpleTypes.putAll(maSimpleTypes); + return aSortedSimpleTypes.entrySet(); + } + + + + + private SimpleTypeContainer (final File aLogFile) + { + maSimpleTypes = new TreeMap<>(); + maLog = new Log(aLogFile); + } + + + + + private void ProcessSimpleType ( + final SimpleType aType, + final SchemaBase aSchemaBase) + { + maSimpleTypes.put( + aType.GetName().GetStateName(), + SimpleTypeDescriptorFactory.CreateSimpleTypeDescriptor( + aType, + aSchemaBase, + maLog)); + } + + + + + private final Map<String,SimpleTypeDescriptor> maSimpleTypes; + private final Log maLog; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeDescriptor.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeDescriptor.java new file mode 100644 index 000000000000..0c415e11a3a7 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeDescriptor.java @@ -0,0 +1,86 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +import org.apache.openoffice.ooxml.schema.misc.Log; +import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; + +public class SimpleTypeDescriptor +{ + public SimpleTypeDescriptor ( + final QualifiedName aName) + { + maName = aName; + maSubTypes = null; + } + + + + + public QualifiedName GetName() + { + return maName; + } + + + + + public void SetSubTypes (final ISimpleTypeNode[] aSubTypes) + { + maSubTypes = aSubTypes; + } + + + + + public ISimpleTypeNode[] GetSubType () + { + return maSubTypes; + } + + + + + @Override + public String toString () + { + return "simple type "+maName; + } + + + + + public void Print (final Log aLog) + { + aLog.printf("%s\n", toString()); + aLog.StartBlock(); + for (final ISimpleTypeNode aSubType : maSubTypes) + aSubType.Print(aLog); + aLog.EndBlock(); + } + + + + + private final QualifiedName maName; + private ISimpleTypeNode[] maSubTypes; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeDescriptorFactory.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeDescriptorFactory.java new file mode 100644 index 000000000000..55c1cf24c145 --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeDescriptorFactory.java @@ -0,0 +1,307 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.openoffice.ooxml.schema.misc.Log; +import org.apache.openoffice.ooxml.schema.model.base.INode; +import org.apache.openoffice.ooxml.schema.model.base.NodeType; +import org.apache.openoffice.ooxml.schema.model.base.NodeVisitorAdapter; +import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; +import org.apache.openoffice.ooxml.schema.model.simple.BuiltIn; +import org.apache.openoffice.ooxml.schema.model.simple.List; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; +import org.apache.openoffice.ooxml.schema.model.simple.SimpleTypeReference; +import org.apache.openoffice.ooxml.schema.model.simple.Union; + +public class SimpleTypeDescriptorFactory + extends NodeVisitorAdapter +{ + class TypeCounter + extends NodeVisitorAdapter + { + public final Map<NodeType,Integer> Counters; + TypeCounter () + { + Counters = new HashMap<>(); + for (final NodeType eType : NodeType.values()) + Counters.put(eType, 0); + } + @Override + public void Default (final INode aNode) + { + Counters.put(aNode.GetNodeType(), Counters.get(aNode.GetNodeType())+1); + } + } + + + + + public static SimpleTypeDescriptor CreateSimpleTypeDescriptor( + final SimpleType aSimpleType, + final SchemaBase aSchemaBase, + final Log aLog) + { + final SimpleTypeDescriptorFactory aFactory = new SimpleTypeDescriptorFactory( + aSchemaBase, + aLog); + + aLog.AddComment("Simple type %s, defined at %s", + aSimpleType.GetName(), + aSimpleType.GetLocation().toString()); + + final ISimpleTypeNode aSubType = aFactory.ProcessSimpleType(aSimpleType); + final SimpleTypeDescriptor aDescriptor = new SimpleTypeDescriptor(aSimpleType.GetName()); + aSubType.AcceptVisitor(new SimpleTypeNodeVisitorAdapter () + { + @Override public void Visit (final UnionNode aType) + { + aDescriptor.SetSubTypes(aType.GetChildren()); + } + + @Override public void Default (final ISimpleTypeNode aType) + { + aDescriptor.SetSubTypes(new ISimpleTypeNode[]{aSubType}); + } + }); + + aDescriptor.Print(aLog); + aLog.printf("\n"); + + return aDescriptor; + } + + + + + private SimpleTypeDescriptorFactory ( + final SchemaBase aSchemaBase, + final Log aLog) + { + maSchemaBase = aSchemaBase; + maLog = aLog; + maResult = null; + } + + + + + ISimpleTypeNode ProcessSimpleType (final SimpleType aSimpleType) + { + return ApplyVisitor(aSimpleType); + } + + + + + @Override + public void Visit (final BuiltIn aNode) + { + assert(aNode.GetChildCount() == 0); + assert(maResult == null); + + maLog.AddComment("builtin %s", aNode.toString()); + + switch(aNode.GetBuiltInType()) + { + case Double: + maResult = new NumberNode<Double>(aNode.GetBuiltInType()); + break; + case Float: + maResult = new NumberNode<Float>(aNode.GetBuiltInType()); + break; + + case Boolean: + maResult = new NumberNode<Boolean>(aNode.GetBuiltInType()); + case Integer: + maResult = new NumberNode<Long>(aNode.GetBuiltInType()); + break; + case Byte: + maResult = new NumberNode<Byte>(aNode.GetBuiltInType()); + case Int: + maResult = new NumberNode<Integer>(aNode.GetBuiltInType()); + case Long: + maResult = new NumberNode<Long>(aNode.GetBuiltInType()); + case Short: + maResult = new NumberNode<Short>(aNode.GetBuiltInType()); + case UnsignedByte: + maResult = new NumberNode<Integer>(aNode.GetBuiltInType()); + case UnsignedInt: + maResult = new NumberNode<Long>(aNode.GetBuiltInType()); + case UnsignedLong: + maResult = new NumberNode<Long>(aNode.GetBuiltInType()); + case UnsignedShort: + maResult = new NumberNode<Integer>(aNode.GetBuiltInType()); + break; + + case AnyURI: + case ID: + case NcName: + case String: + case Token: + maResult = new StringNode(aNode.GetBuiltInType()); + break; + + case Base64Binary: + case HexBinary: + maResult = new BlobNode(aNode.GetBuiltInType()); + break; + + case DateTime: + maResult = new DateTimeNode(aNode.GetBuiltInType()); + break; + + default: + throw new RuntimeException(aNode.toString()+" is not supported"); + } + } + + + + + @Override + public void Visit (final List aNode) + { + maLog.AddComment("list of type %s", aNode.GetItemType().toString()); + maLog.StartBlock(); + final ISimpleTypeNode aItemType = ApplyVisitor(maSchemaBase.GetSimpleTypeForName(aNode.GetItemType())); + maLog.EndBlock(); + + aItemType.SetIsList(); + maResult = aItemType; + } + + + + + @Override + public void Visit (final Restriction aNode) + { + assert(aNode.GetChildCount() == 0); + + maLog.AddComment("%s", aNode.toString()); + + final INode aBaseType = maSchemaBase.GetSimpleTypeForName(aNode.GetBaseType()); + if (aBaseType == null) + throw new RuntimeException("got no type for name "+aNode.GetBaseType()); + maLog.StartBlock(); + maResult = ApplyVisitor(aBaseType); + maLog.EndBlock(); + maResult.ApplyRestriction( + aNode, + maSchemaBase.AttributeValueToIdMap); + } + + + + + @Override + public void Visit (final SimpleType aNode) + { + maLog.AddComment(aNode.toString()); + + assert(aNode.GetChildCount() <= 1); + switch(aNode.GetChildCount()) + { + case 0: + maResult = null; + break; + case 1: + maLog.StartBlock(); + maResult = ApplyVisitor(aNode.GetOnlyChild()); + maLog.EndBlock(); + break; + default: + throw new RuntimeException(); + } + } + + + + + @Override + public void Visit (final SimpleTypeReference aNode) + { + maLog.AddComment("reference to %s", aNode.GetReferencedTypeName()); + + maLog.StartBlock(); + maResult = ApplyVisitor(aNode.GetReferencedNode(maSchemaBase)); + maLog.EndBlock(); + } + + + + + @Override + public void Visit (final Union aNode) + { + maLog.AddComment("union"); + + final UnionNode aUnion = new UnionNode(); + + // Make sure that all children have compatible types and value sets. + maLog.StartBlock(); + + for (final INode aChild : aNode.GetChildren()) + { + aUnion.AddNode(ApplyVisitor(aChild)); + } + + maLog.EndBlock(); + + maResult = aUnion; + } + + + + + @Override + public void Default (final INode aNode) + { + switch(aNode.GetNodeType()) + { + default: + throw new RuntimeException(aNode.GetNodeType() +" is not yet supported"); + } + } + + + + + ISimpleTypeNode ApplyVisitor (final INode aNode) + { + aNode.AcceptVisitor(this); + final ISimpleTypeNode aResult = maResult; + maResult = null; + return aResult; + } + + + + + private final SchemaBase maSchemaBase; + private final Log maLog; + private ISimpleTypeNode maResult; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeNodeVisitorAdapter.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeNodeVisitorAdapter.java new file mode 100644 index 000000000000..9e30d653bb9a --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/SimpleTypeNodeVisitorAdapter.java @@ -0,0 +1,60 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +public class SimpleTypeNodeVisitorAdapter + implements ISimpleTypeNodeVisitor +{ + @Override + public void Visit(BlobNode aType) + { + Default(aType); + } + + @Override + public void Visit(DateTimeNode aType) + { + Default(aType); + } + + @Override + public void Visit(NumberNode<?> aType) + { + Default(aType); + } + + @Override + public void Visit(StringNode aType) + { + Default(aType); + } + + @Override + public void Visit(UnionNode aType) + { + Default(aType); + } + + public void Default (final ISimpleTypeNode aType) + { + } +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/StringNode.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/StringNode.java new file mode 100644 index 000000000000..b88da5c7e40a --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/StringNode.java @@ -0,0 +1,269 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.apache.openoffice.ooxml.schema.misc.Log; +import org.apache.openoffice.ooxml.schema.model.simple.BuiltInType; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; + +public class StringNode + implements ISimpleTypeNode +{ + public StringNode ( + final BuiltInType eType) + { + meType = eType; + mbIsList = false; + } + + + + + @Override + public void ApplyRestriction ( + final Restriction aRestriction, + final Map<String,Integer> aValueToIdMap) + { + assert( ! aRestriction.HasFeature( + Restriction.MinExclusiveBit + | Restriction.MinInclusiveBit + | Restriction.MaxExclusiveBit + | Restriction.MaxInclusiveBit)); + + if (aRestriction.HasFeature(Restriction.EnumerationBit)) + { + if (aRestriction.HasFeature(Restriction.LengthBit|Restriction.MinLengthBit|Restriction.MaxLengthBit)) + for (final String sValue : aRestriction.GetEnumeration()) + assert(CheckLengthRestriction(sValue, aRestriction)); + maEnumerationValues = new TreeSet<>(); + maEnumerationValueIds = new HashSet<>(); + for (final String sValue : aRestriction.GetEnumeration()) + { + if ( ! aValueToIdMap.containsKey(sValue)) + aValueToIdMap.put(sValue, aValueToIdMap.size()); + maEnumerationValues.add(sValue); + maEnumerationValueIds.add(aValueToIdMap.get(sValue)); + } + } + else if (aRestriction.HasFeature(Restriction.PatternBit)) + { + msPattern = aRestriction.GetPattern(); + // Make the regular expression understandable by Java (by replacing + // character class names like IsBasicLatin to InBasicLatin). + try + { + maPattern = Pattern.compile(msPattern.replace("\\p{Is", "\\p{In"), Pattern.UNICODE_CHARACTER_CLASS); + } + catch (PatternSyntaxException aException) + { + aException.printStackTrace(); + } + } + else if (aRestriction.HasFeature(Restriction.LengthBit|Restriction.MinLengthBit|Restriction.MaxLengthBit)) + { + if (aRestriction.HasFeature(Restriction.LengthBit)) + mnMinimumLength = mnMaximumLength = aRestriction.GetLength(); + if (aRestriction.HasFeature(Restriction.MinLengthBit)) + mnMinimumLength = aRestriction.GetMinimumLength(); + if (aRestriction.HasFeature(Restriction.MaxLengthBit)) + mnMaximumLength = aRestriction.GetMaximumLength(); + } + else + { + // no restriction. + assert(aRestriction.GetFeatureBits() == 0); + } + } + + + + + @Override + public void Print (final Log aLog) + { + aLog.println(toString()); + } + + + + + @Override + public String toString () + { + final StringBuffer aBuffer = new StringBuffer(); + aBuffer.append(String.format("string (%s)", meType)); + if (maEnumerationValueIds != null) + { + aBuffer.append(" ["); + boolean bIsFirst = true; + for (final String sValue : maEnumerationValues) + { + if (bIsFirst) + bIsFirst = false; + else + aBuffer.append(", "); + aBuffer.append(sValue); + } + aBuffer.append("]"); + } + else if (maPattern != null) + { + aBuffer.append("pattern=\""); + aBuffer.append(maPattern); + aBuffer.append("\""); + } + return aBuffer.toString(); + } + + + + + @Override + public boolean IsList () + { + return mbIsList; + } + + + + + @Override + public void SetIsList () + { + mbIsList = true; + } + + + + + /** Try to join the called and the given string types. + * If that is possible then return the resulting type. + * Otherwise return null. + */ + public ISimpleTypeNode Join (final StringNode aType) + { + if (maEnumerationValues!=null && aType.maEnumerationValues!=null) + { + // Join the enumeration values. + maEnumerationValues.addAll(aType.maEnumerationValues); + maEnumerationValueIds.addAll(aType.maEnumerationValueIds); + return this; + } + else + return null; + } + + + public enum RestrictionType + { + Pattern, + Enumeration, + Length, + None + } + public RestrictionType GetRestrictionType () + { + if (maEnumerationValueIds != null) + return RestrictionType.Enumeration; + else if (maPattern != null) + return RestrictionType.Pattern; + else if (mnMinimumLength != null) + return RestrictionType.Length; + else + return RestrictionType.None; + } + + + + + public Set<Integer> GetEnumerationRestriction () + { + final Set<Integer> aSortedIds = new TreeSet<>(); + aSortedIds.addAll(maEnumerationValueIds); + return aSortedIds; + } + + + + + public String GetPatternRestriction () + { + return msPattern; + } + + + + + public int[] GetLengthRestriction () + { + return new int[]{mnMinimumLength, mnMaximumLength}; + } + + + + + private boolean CheckLengthRestriction ( + final String sValue, + final Restriction aRestriction) + { + final int nValueLength = sValue.length(); + if (aRestriction.HasFeature(Restriction.LengthBit)) + return nValueLength == aRestriction.GetLength(); + else if (aRestriction.HasFeature(Restriction.MinLengthBit | Restriction.MaxLengthBit)) + return nValueLength>=aRestriction.GetMinimumLength() + && nValueLength<=aRestriction.GetMaximumLength(); + else if (aRestriction.HasFeature(Restriction.MinLengthBit)) + return nValueLength>=aRestriction.GetMinimumLength(); + else if (aRestriction.HasFeature(Restriction.MaxLengthBit)) + return nValueLength<=aRestriction.GetMaximumLength(); + else + throw new RuntimeException(); + } + + + + + @Override + public void AcceptVisitor (final ISimpleTypeNodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + private final BuiltInType meType; + private Set<String> maEnumerationValues; + private Set<Integer> maEnumerationValueIds; + private Pattern maPattern; + private String msPattern; + private Integer mnMinimumLength; + private Integer mnMaximumLength; + private boolean mbIsList; +} diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/UnionNode.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/UnionNode.java new file mode 100644 index 000000000000..94d428ae333a --- /dev/null +++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/simple/UnionNode.java @@ -0,0 +1,151 @@ +/************************************************************** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*************************************************************/ + +package org.apache.openoffice.ooxml.schema.simple; + +import java.util.Map; +import java.util.Vector; + +import org.apache.openoffice.ooxml.schema.misc.Log; +import org.apache.openoffice.ooxml.schema.model.simple.Restriction; + +public class UnionNode + implements ISimpleTypeNode +{ + UnionNode () + { + maTypes = new Vector<>(); + mbIsList = false; + } + + + + + @Override + public void ApplyRestriction ( + final Restriction aNode, + final Map<String,Integer> aValueToIdMap) + { + throw new RuntimeException("can not handle restriction on union"); + } + + + + + + public void AddNode (final ISimpleTypeNode aType) + { + if (aType instanceof UnionNode) + { + // Integrate union of child into this union. + final UnionNode aChildUnion = (UnionNode)aType; + for (final ISimpleTypeNode aChildChild : aChildUnion.maTypes) + maTypes.add(aChildChild); + } + else if (aType instanceof StringNode) + { + // Is there already a string child? + for (int nIndex=0; nIndex<maTypes.size(); ++nIndex) + { + final ISimpleTypeNode aChild = maTypes.get(nIndex); + + if (aChild instanceof StringNode) + { + // Yes. Can it be joined with the new child? + final ISimpleTypeNode aNewChild = ((StringNode)aChild).Join((StringNode)aType); + if (aNewChild != null) + { + // Yes. Replace the child with the joined child. + maTypes.set(nIndex, aNewChild); + return; + } + } + } + // When we reach this point then there was no join possible. + // Just add the new type. + maTypes.add(aType); + + } + else + maTypes.add(aType); + } + + + + + @Override + public void Print (final Log aLog) + { + aLog.printf("union of %d sub types\n", maTypes.size()); + aLog.StartBlock(); + for (final ISimpleTypeNode aType : maTypes) + aType.Print(aLog); + aLog.EndBlock(); + } + + + + + @Override + public boolean IsList () + { + return mbIsList; + } + + + + + @Override + public void SetIsList () + { + mbIsList = true; + } + + + + + @Override + public String toString () + { + return "union"; + } + + + ISimpleTypeNode[] GetChildren () + { + return maTypes.toArray(new ISimpleTypeNode[maTypes.size()]); + } + + + + + @Override + public void AcceptVisitor (final ISimpleTypeNodeVisitor aVisitor) + { + aVisitor.Visit(this); + } + + + + + private final Vector<ISimpleTypeNode> maTypes; + private boolean mbIsList; +} |