diff options
Diffstat (limited to 'ooxml/source')
168 files changed, 22727 insertions, 0 deletions
diff --git a/ooxml/source/framework/JavaOOXMLParser/.classpath b/ooxml/source/framework/JavaOOXMLParser/.classpath new file mode 100644 index 000000000000..fb565a588d8a --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/.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/JavaOOXMLParser/.project b/ooxml/source/framework/JavaOOXMLParser/.project new file mode 100644 index 000000000000..409d549457ee --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>JavaOOXMLParser</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/JavaOOXMLParser/.settings/org.eclipse.jdt.core.prefs b/ooxml/source/framework/JavaOOXMLParser/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000000..7341ab1683c4 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/.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/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/AcceptingStateTable.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/AcceptingStateTable.java new file mode 100644 index 000000000000..c11ea4405fe6 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/AcceptingStateTable.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.parser; + +import java.util.HashSet; +import java.util.Set; + +/** List of all accepting states. + * + * The accepting status of states is important when a closing tag is seen. + * It denotes the end of the input stream for the state machine of the currently + * processed element. It is an error when the current state is not accepting + * when a closing tag is processed. + */ +public class AcceptingStateTable +{ + public AcceptingStateTable (final Iterable<String[]> aData) + { + maAcceptingStates = new HashSet<>(); + + for (final String[] aLine : aData) + { + // Create new transition. + final int nStateId = Integer.parseInt(aLine[1]); + + maAcceptingStates.add(nStateId); + } + } + + + + + public boolean Contains (final int nStateId) + { + return maAcceptingStates.contains(nStateId); + } + + + + + public int GetAcceptingStateCount () + { + return maAcceptingStates.size(); + } + + + + + private final Set<Integer> maAcceptingStates; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/ElementContext.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/ElementContext.java new file mode 100644 index 000000000000..2114dd75af4e --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/ElementContext.java @@ -0,0 +1,88 @@ +/************************************************************** +* +* 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.parser; + +import org.apache.openoffice.ooxml.parser.attribute.AttributeValues; + +/** Context that has the same life time (by default) as the element it represents. + * Gives access to the attribute values and the parent context. + */ +public class ElementContext +{ + ElementContext ( + final String sElementName, + final String sTypeName, + final boolean bIsSkipping, + final AttributeValues aValues, + final ElementContext aParentContext) + { + msElementName = sElementName; + msTypeName = sTypeName; + mbIsSkipping = bIsSkipping; + maAttributeValues = aValues; + maParentContext = aParentContext; + } + + + + + public String GetElementName () + { + return msElementName; + } + + + + + public String GetTypeName () + { + return msTypeName; + } + + + + + public AttributeValues GetAttributes () + { + return maAttributeValues; + } + + + + + /** Return the context of the parent element. + * Can be null when there is no parent element. + */ + public ElementContext GetParentContext () + { + return maParentContext; + } + + + + + private final String msElementName; + private final String msTypeName; + private final boolean mbIsSkipping; + private final AttributeValues maAttributeValues; + private final ElementContext maParentContext; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/Log.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/Log.java new file mode 100644 index 000000000000..cefdd513f8b4 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/Log.java @@ -0,0 +1,130 @@ +/************************************************************** +* +* 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.parser; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class Log +{ + public static Log Std = new Log(System.out); + public static Log Err = new Log(System.err); + public static Log Dbg = Std; + + + public Log (final OutputStream aOut) + { + maOut = aOut; + msIndentation = ""; + } + + + + + public Log (final String sFilename) + { + this(CreateFileOutputStream(sFilename)); + } + + + + + public Log (final File aFile) + { + this(CreateFileOutputStream(aFile)); + } + + + + + private static OutputStream CreateFileOutputStream (final File aFile) + { + try + { + return new FileOutputStream(aFile); + } + catch (final Exception aException) + { + aException.printStackTrace(); + return null; + } + } + + + + + public void printf (final String sFormat, final Object ... aArgumentList) + { + try + { + maOut.write(msIndentation.getBytes()); + maOut.write(String.format(sFormat, aArgumentList).getBytes()); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + + + + public void IncreaseIndentation () + { + msIndentation += " "; + } + + + + + public void DecreaseIndentation () + { + msIndentation = msIndentation.substring(4); + } + + + + + private static OutputStream CreateFileOutputStream (final String sFilename) + { + OutputStream aOut; + try + { + aOut = new FileOutputStream(sFilename); + return aOut; + } + catch (FileNotFoundException e) + { + e.printStackTrace(); + return null; + } + } + + + + + private final OutputStream maOut; + private String msIndentation; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/NameMap.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/NameMap.java new file mode 100644 index 000000000000..a8af8f93ce51 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/NameMap.java @@ -0,0 +1,113 @@ +/************************************************************** +* +* 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.parser; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Vector; + +public class NameMap +{ + NameMap (final Vector<String[]> aData) + { + maNameToIdMap = new HashMap<>(); + maIdToNameMap = new Vector<>(); + + for (final String[] aLine : aData) + { + final int nId = Integer.parseInt(aLine[1]); + + maNameToIdMap.put(aLine[2], nId); + + if (maIdToNameMap.size() <= nId) + maIdToNameMap.setSize(nId+1); + maIdToNameMap.set(nId, aLine[2]); + } + } + + + + + public int GetIdForName ( + final String sName) + { + if ( ! maNameToIdMap.containsKey(sName)) + throw new RuntimeException("token '"+sName+"' is not known"); + + return maNameToIdMap.get(sName); + } + + + + + public int GetIdForOptionalName ( + final String sName) + { + final Integer aId = maNameToIdMap.get(sName); + if (aId == null) + return -1; + else + return aId; + } + + + + + public String GetNameForId (final int nId) + { + if (nId == -1) + return "<none>"; + else + return maIdToNameMap.get(nId); + } + + + + + public int GetNameCount () + { + return maIdToNameMap.size(); + } + + + + + /** Return the ids of all states whose names match the given pattern. + */ + public Vector<Integer> GetMatchingStateIds (final String sPattern) + { + final Vector<Integer> aStateIds = new Vector<>(); + for (final Entry<String,Integer> aEntry : maNameToIdMap.entrySet()) + { + if (aEntry.getKey().matches(sPattern)) + aStateIds.add(aEntry.getValue()); + } + return aStateIds; + } + + + + + private final Map<String,Integer> maNameToIdMap; + private final Vector<String> maIdToNameMap; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/NamespaceMap.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/NamespaceMap.java new file mode 100644 index 000000000000..d5a2af818c53 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/NamespaceMap.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.parser; + +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +public class NamespaceMap +{ + public class NamespaceDescriptor + { + NamespaceDescriptor (final String sPrefix, final int nId) + { + Prefix = sPrefix; + Id = nId; + } + public final String Prefix; + public final int Id; + } + NamespaceMap (final Vector<String[]> aData) + { + maUriToDescriptorMap = new HashMap<>(); + maIdToDescriptorMap = new HashMap<>(); + + for (final String[] aLine : aData) + { + final int nId = Integer.parseInt(aLine[2]); + final NamespaceDescriptor aDescriptor = new NamespaceDescriptor(aLine[1], nId); + maUriToDescriptorMap.put( + aLine[3], + aDescriptor); + maIdToDescriptorMap.put( + nId, + aDescriptor); + } + } + + + + + public NamespaceDescriptor GetDescriptorForURI (final String sURI) + { + if (sURI == null) + throw new RuntimeException("namespace is null"); + if ( ! maUriToDescriptorMap.containsKey(sURI)) + throw new RuntimeException("namespace '"+sURI+"' is not known"); + return maUriToDescriptorMap.get(sURI); + } + + + + + public NamespaceDescriptor GetDescriptorForId (final int nId) + { + return maIdToDescriptorMap.get(nId); + } + + + + + public int GetNamespaceCount () + { + return maUriToDescriptorMap.size(); + } + + + + + private final Map<String,NamespaceDescriptor> maUriToDescriptorMap; + private final Map<Integer,NamespaceDescriptor> maIdToDescriptorMap; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/OOXMLParser.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/OOXMLParser.java new file mode 100644 index 000000000000..369f5c3daf42 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/OOXMLParser.java @@ -0,0 +1,223 @@ +/************************************************************** +* +* 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.parser; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import javax.xml.stream.Location; + +import org.apache.openoffice.ooxml.parser.action.ActionManager; +import org.apache.openoffice.ooxml.parser.action.ActionTrigger; +import org.apache.openoffice.ooxml.parser.action.IAction; + +/** This OOXML parser is based on the output of the schema parser. + * It exists to debug the schema parser and as illustration and preparation of + * the C++ parse (yet to come.) + * Because of this, the parser data (set of states and transitions) are + * read at runtime while a real parser would do that at compile time. + */ +public class OOXMLParser +{ + class ActionContext + { + public Map<String,Integer> TypeCounts = new TreeMap<>(); + } + /** The parser is called with two arguments: + * - A path to where the parser tables with the states and transitions can + * be found. + * - The XML input file or Zip stream to parse. + * The syntax for a Zip stream contains a '#' that separates the filename + * to its left from the entry name to its right. + */ + public static void main (final String ... aArgumentList) + { + if (aArgumentList.length<2 ||aArgumentList.length>3) + throw new RuntimeException("usage: OOXMLParser <parser-tables-path> <XML-input-file> <log-file>?"); + + if (aArgumentList.length == 3) + { + final File aLogFile = new File(aArgumentList[2]); + Log.Dbg = new Log(aLogFile); + System.out.printf("writing log data to %s\n", aLogFile.toString()); + } + else + { + Log.Dbg = null; + System.out.printf("writing no log data\n"); + } + + new OOXMLParser(aArgumentList[0], aArgumentList[1]); + } + + + + private OOXMLParser ( + final String sParseTableFilename, + final String sInputFilename) + { + long nStartTime = System.currentTimeMillis(); + final StateMachine aMachine = new StateMachine(new File(sParseTableFilename), null); + final InputStream aIn = GetInputStream(sInputFilename); + long nEndTime = System.currentTimeMillis(); + + final ActionContext aActionContext = new ActionContext(); + AddSomeActions(aMachine.GetActionManager(), aActionContext); + + System.out.printf("initialzed parser in %fs\n", (nEndTime-nStartTime)/1000.0); + + try + { + nStartTime = System.currentTimeMillis(); + final Parser aParser = new Parser(aMachine, aIn); + aParser.Parse(); + final int nElementCount = aParser.GetElementCount(); + nEndTime = System.currentTimeMillis(); + System.out.printf("parsed %d elements in %fs\n", + nElementCount, + (nEndTime-nStartTime)/1000.0); + + System.out.printf("%d different elements found:\n", aActionContext.TypeCounts.size()); + for (final Entry<String, Integer> aEntry : aActionContext.TypeCounts.entrySet()) + { + System.out.printf("%-32s : %6d\n", aEntry.getKey(), aEntry.getValue()); + } + } + catch (final Exception aException) + { + aException.printStackTrace(); + } + } + + + + + private static void AddSomeActions ( + final ActionManager aActionManager, + final ActionContext aActionContext) + { + aActionManager.AddElementStartAction( + "*", + new IAction() + { + @Override public void Run( + final ActionTrigger eTrigger, + final ElementContext aContext, + final String sText, + final Location aStartLocation, + final Location aEndLocation) + { + Integer nValue = aActionContext.TypeCounts.get(aContext.GetTypeName()); + if (nValue == null) + nValue = 1; + else + ++nValue; + aActionContext.TypeCounts.put(aContext.GetTypeName(), nValue); + } + } + ); + aActionManager.AddElementStartAction( + ".*CT_Shd", + new IAction() + { + @Override public void Run( + final ActionTrigger eTrigger, + final ElementContext aContext, + final String sText, + final Location aStartLocation, + final Location aEndLocation) + { + System.out.printf("processing %s of element %s at position %d\n", + eTrigger, + aContext.GetElementName(), + aStartLocation.getCharacterOffset()); + + if (aContext.GetAttributes().GetAttributeCount() == 0) + System.out.printf(" no attributes\n"); + else + for (final Entry<String,String> aAttribute : aContext.GetAttributes().GetAttributes()) + System.out.printf(" %s -> %s\n", aAttribute.getKey(), aAttribute.getValue()); + } + } + ); + aActionManager.AddTextAction( + ".*CT_Text", + new IAction() + { + @Override public void Run( + final ActionTrigger eTrigger, + final ElementContext aContext, + final String sText, + final Location aStartLocation, + final Location aEndLocation) + { +// System.out.printf("%s text \"%s\"\n", aContext.GetTypeName(), sText.replace("\n", "\\n")); + } + } + ); + } + + + + + private static InputStream GetInputStream (final String sInputName) + { + final InputStream aIn; + try + { + final int nSeparator = sInputName.indexOf('#'); + if (nSeparator >= 0) + { + // Split the input name into the file name of the archive and the + // name of a zip entry. + final String sArchiveName = sInputName.substring(0, nSeparator); + String sEntryName = sInputName.substring(nSeparator+1); + + // Normalize and cleanup the entry name. + sEntryName = sEntryName.replace('\\', '/'); + if (sEntryName.startsWith("/")) + sEntryName = sEntryName.substring(1); + + final ZipFile aZipFile = new ZipFile(new File(sArchiveName)); + final ZipEntry aZipEntry = aZipFile.getEntry(sEntryName); + aIn = aZipFile.getInputStream(aZipEntry); + } + else + { + // The input name points to a plain XML file. + aIn = new FileInputStream(sInputName); + } + } + catch (final Exception aException) + { + aException.printStackTrace(); + return null; + } + return aIn; + } +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/ParseException.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/ParseException.java new file mode 100644 index 000000000000..ae07f9ca1fec --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/ParseException.java @@ -0,0 +1,42 @@ +/************************************************************** +* +* 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.parser; + +import javax.xml.stream.Location; + +@SuppressWarnings("serial") +public class ParseException + extends RuntimeException +{ + public ParseException ( + final Exception aException, + final Location aLocation) + { + super(aException); + Location = aLocation; + } + + + + + public final Location Location; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/ParseTableReader.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/ParseTableReader.java new file mode 100644 index 000000000000..0288886c5342 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/ParseTableReader.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.parser; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +/** A simple reader for the parse table data that allows simple filtering on the + * first word in each line. + * + * Lines that only contain comments or whitespace are ignored. + * + */ +public class ParseTableReader +{ + public ParseTableReader (final File aFile) + { + maSections = new HashMap<>(); + + try + { + final BufferedReader aReader = new BufferedReader(new FileReader(aFile)); + + while (true) + { + final String sLine = aReader.readLine(); + if (sLine == null) + break; + else if (sLine.startsWith("#")) + continue; + else if (sLine.isEmpty()) + continue; + + final String[] aLineParts = sLine.split("\\s+"); + for (int nIndex=0; nIndex<aLineParts.length; ++nIndex) + { + final String sPart = aLineParts[nIndex]; + if (sPart.isEmpty()) + { + throw new RuntimeException(); + } + else if (sPart.charAt(0) == '"') + { + // Remove leading and trailing quotes, unquote spaces. + aLineParts[nIndex] = sPart.substring(1, sPart.length()-1).replace("%20", " ").replace(""", "\""); + } + } + GetSection(aLineParts[0]).add(aLineParts); + } + + aReader.close(); + } + catch (final Exception aException) + { + throw new RuntimeException(aException); + } + } + + + + + public Vector<String[]> GetSection (final String sSectionName) + { + Vector<String[]> aSection = maSections.get(sSectionName); + if (aSection == null) + { + aSection = new Vector<>(); + maSections.put(sSectionName, aSection); + } + return aSection; + } + + + + + private final Map<String,Vector<String[]>> maSections; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/Parser.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/Parser.java new file mode 100644 index 000000000000..cd51087f3c3d --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/Parser.java @@ -0,0 +1,249 @@ +/************************************************************** +* +* 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.parser; + +import java.io.InputStream; +import java.util.Vector; + +import javax.xml.stream.Location; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.apache.openoffice.ooxml.parser.action.ActionManager; +import org.apache.openoffice.ooxml.parser.attribute.AttributeProvider; + +/** This is the actual parser (where OOXMLParser is the front end that handles + * parameters given to the main method). + */ +public class Parser +{ + public Parser ( + final StateMachine aMachine, + final InputStream aIn) + { + maMachine = aMachine; + maReader = GetStreamReader(aIn, "input"); + mnElementCount = 0; + } + + + + + public void Parse () + { + Location aLocation = null; + try + { + final AttributeProvider aAttributeProvider = new AttributeProvider(maReader); + while (maReader.hasNext()) + { + aLocation = maReader.getLocation(); + final int nCode = maReader.next(); + switch(nCode) + { + case XMLStreamReader.START_ELEMENT: + ++mnElementCount; + if (maMachine.IsInSkipState()) + { + if (Log.Dbg != null) + Log.Dbg.printf("is skip state -> starting to skip\n"); + Skip(); + } + else if ( ! maMachine.ProcessStartElement( + maReader.getNamespaceURI(), + maReader.getLocalName(), + aLocation, + maReader.getLocation(), + aAttributeProvider)) + { + if (Log.Dbg != null) + Log.Dbg.printf("starting to skip to recover from error\n"); + Skip(); + } + break; + + case XMLStreamReader.END_ELEMENT: + maMachine.ProcessEndElement( + maReader.getNamespaceURI(), + maReader.getLocalName(), + aLocation, + maReader.getLocation()); + break; + + case XMLStreamReader.CHARACTERS: + maMachine.ProcessCharacters( + maReader.getText(), + aLocation, + maReader.getLocation()); + break; + + case XMLStreamReader.END_DOCUMENT: + break; + + default: + Log.Err.printf("can't handle XML event of type %d\n", nCode); + } + } + + maReader.close(); + } + catch (final XMLStreamException aException) + { + aException.printStackTrace(); + } + catch (final Exception aException) + { + throw new ParseException( + aException, + aLocation); + } + } + + + + + + + public int GetElementCount () + { + return mnElementCount; + } + + + + + private void Skip () + { + if (Log.Dbg != null) + { + Log.Dbg.printf("starting to skip on %s at L%dC%d\n", + maReader.getLocalName(), + maReader.getLocation().getLineNumber(), + maReader.getLocation().getColumnNumber()); + Log.Dbg.IncreaseIndentation(); + } + + // We are called when processing a start element. This means that we are + // already at relative depth 1. + int nRelativeDepth = 1; + try + { + while (maReader.hasNext()) + { + final int nCode = maReader.next(); + switch (nCode) + { + case XMLStreamReader.START_ELEMENT: + ++nRelativeDepth; + ++mnElementCount; + if (Log.Dbg != null) + { + Log.Dbg.printf("skipping start element %s\n", maReader.getLocalName()); + Log.Dbg.IncreaseIndentation(); + } + break; + + case XMLStreamReader.END_ELEMENT: + --nRelativeDepth; + if (Log.Dbg != null) + Log.Dbg.DecreaseIndentation(); + if (nRelativeDepth <= 0) + { + if (Log.Dbg != null) + Log.Dbg.printf("leaving skip mode on %s\n", maReader.getLocalName()); + return; + } + break; + + case XMLStreamReader.END_DOCUMENT: + throw new RuntimeException("saw end of document while skipping elements\n"); + + case XMLStreamReader.CHARACTERS: + SkipText(maReader.getText()); + break; + + default: + if (Log.Dbg != null) + Log.Dbg.printf("%s\n", nCode); + break; + } + } + } + catch (final XMLStreamException aException) + { + aException.printStackTrace(); + } + } + + + + + private void SkipText (final String sText) + { + if (Log.Dbg != null) + Log.Dbg.printf("skipping text [%s]\n", sText.replace("\n", "\\n")); + } + + + + + private XMLStreamReader GetStreamReader ( + final InputStream aIn, + final String sDescription) + { + if (aIn == null) + return null; + + try + { + 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); + + return (XMLStreamReader)aFactory.createXMLStreamReader( + sDescription, + aIn); + } + catch (final Exception aException) + { + aException.printStackTrace(); + return null; + } + } + + + + + public ActionManager GetActionManager() + { + return maMachine.GetActionManager(); + } + + + + + private final XMLStreamReader maReader; + private final StateMachine maMachine; + private int mnElementCount; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/SkipStateTable.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/SkipStateTable.java new file mode 100644 index 000000000000..0171b743a811 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/SkipStateTable.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.parser; + +import java.util.HashSet; +import java.util.Set; + +/** Table of all skip states. + * + * A skip state corresponds to the 'any' element in the schemas. + * It means that the content of the element is specified by an extension of the + * schema which may or may not be known at parse time. + * At the moment the whole element is skipped, i.e. ignored. + * + */ +public class SkipStateTable +{ + public SkipStateTable (final Iterable<String[]> aData) + { + maSkipStates = new HashSet<>(); + + for (final String[] aLine : aData) + { + // Create new transition. + final int nStateId = Integer.parseInt(aLine[1]); + + maSkipStates.add(nStateId); + } + } + + + + + public boolean Contains (final int nStateId) + { + return maSkipStates.contains(nStateId); + } + + + + + public int GetSkipStateCount () + { + return maSkipStates.size(); + } + + + + + private final Set<Integer> maSkipStates; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/StateMachine.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/StateMachine.java new file mode 100644 index 000000000000..39ff4dfd8137 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/StateMachine.java @@ -0,0 +1,330 @@ +/************************************************************** +* +* 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.parser; + +import java.io.File; +import java.util.Stack; +import java.util.Vector; + +import javax.xml.stream.Location; + +import org.apache.openoffice.ooxml.parser.action.ActionManager; +import org.apache.openoffice.ooxml.parser.action.ActionTrigger; +import org.apache.openoffice.ooxml.parser.action.IAction; +import org.apache.openoffice.ooxml.parser.attribute.AttributeManager; +import org.apache.openoffice.ooxml.parser.attribute.AttributeProvider; +import org.apache.openoffice.ooxml.parser.attribute.AttributeValues; +import org.apache.openoffice.ooxml.parser.type.SimpleTypeManager; + +/** The state machine is initialized at creation from the data tables + * created previously by a stack automaton. + */ +public class StateMachine +{ + public StateMachine ( + final File aParseTableFile, + final Vector<String> aErrorsAndWarnings) + { + if (Log.Dbg != null) + Log.Dbg.printf("reading parse tables from %s\n", aParseTableFile.toString()); + + final ParseTableReader aReader = new ParseTableReader(aParseTableFile); + maNamespaceMap = new NamespaceMap(aReader.GetSection("namespace")); + maNameMap = new NameMap(aReader.GetSection("name")); + maStateNameMap = new NameMap(aReader.GetSection("state-name")); + maTransitions = new TransitionTable(aReader.GetSection("transition")); + maSkipStates = new SkipStateTable(aReader.GetSection("skip")); + maAttributeValueMap = new NameMap(aReader.GetSection("attribute-value")); + maAcceptingStates = new AcceptingStateTable(aReader.GetSection("accepting-state")); + maSimpleTypeManager = new SimpleTypeManager( + aReader.GetSection("simple-type"), + maAttributeValueMap); + maAttributeManager = new AttributeManager( + aReader.GetSection("attribute"), + maNamespaceMap, + maNameMap, + maStateNameMap, + maSimpleTypeManager, + aErrorsAndWarnings); + mnStartStateId = Integer.parseInt(aReader.GetSection("start-state").firstElement()[1]); + mnEndStateId = Integer.parseInt(aReader.GetSection("end-state").firstElement()[1]); + + mnCurrentStateId = mnStartStateId; + maStateStack = new Stack<>(); + maElementContextStack = new Stack<>(); + maActionManager = new ActionManager(maStateNameMap); + maErrorsAndWarnings = aErrorsAndWarnings; + + if (Log.Dbg != null) + { + Log.Dbg.printf("read %d namespace, %d names, %d states (%d skip, %d accept), %d transitions and %d attributes\n", + maNamespaceMap.GetNamespaceCount(), + maNameMap.GetNameCount(), + maStateNameMap.GetNameCount(), + maSkipStates.GetSkipStateCount(), + maAcceptingStates.GetAcceptingStateCount(), + maTransitions.GetTransitionCount(), + maAttributeManager.GetAttributeCount()); + Log.Dbg.printf("starting in state _start_ (%d)\n", mnCurrentStateId); + } + } + + + + + public boolean ProcessStartElement ( + final String sNamespaceURI, + final String sElementName, + final Location aStartLocation, + final Location aEndLocation, + final AttributeProvider aAttributes) + { + boolean bResult = false; + + try + { + final NamespaceMap.NamespaceDescriptor aNamespaceDescriptor = maNamespaceMap.GetDescriptorForURI(sNamespaceURI); + final int nElementNameId = maNameMap.GetIdForName(sElementName); + if (Log.Dbg != null) + Log.Dbg.printf("%s:%s(%d:%d) L%dC%d\n", + aNamespaceDescriptor.Prefix, + sElementName, + aNamespaceDescriptor.Id, + nElementNameId, + aStartLocation.getLineNumber(), + aStartLocation.getColumnNumber()); + + final Transition aTransition = maTransitions.GetTransition( + mnCurrentStateId, + aNamespaceDescriptor.Id, + nElementNameId); + if (aTransition == null) + { + final String sText = String.format( + "can not find transition for state %s(%d) and element %s:%s(%d:%d) at L%dC%d\n", + maStateNameMap.GetNameForId(mnCurrentStateId), + mnCurrentStateId, + aNamespaceDescriptor.Prefix, + maNameMap.GetNameForId(nElementNameId), + aNamespaceDescriptor.Id, + nElementNameId, + aStartLocation.getLineNumber(), + aStartLocation.getColumnNumber()); + Log.Err.printf(sText); + if (Log.Dbg != null) + Log.Dbg.printf(sText); + } + else + { + if (Log.Dbg != null) + { + Log.Dbg.printf(" %s(%d) -> %s(%d) via %s(%d)", + maStateNameMap.GetNameForId(mnCurrentStateId), + mnCurrentStateId, + maStateNameMap.GetNameForId(aTransition.GetEndStateId()), + aTransition.GetEndStateId(), + maStateNameMap.GetNameForId(aTransition.GetActionId()), + aTransition.GetActionId()); + Log.Dbg.printf("\n"); + } + + // Follow the transition to its end state but first process its + // content. We do that by + + if (Log.Dbg != null) + Log.Dbg.IncreaseIndentation(); + + // a) pushing the end state to the state stack so that on the + // end tag that corresponds to the current start tag it will become the current state. + maStateStack.push(aTransition.GetEndStateId()); + + // b) entering the state that corresponds to start tag that + // we are currently processing. + mnCurrentStateId = aTransition.GetActionId(); + + // c) Prepare the attributes and store them in the new element context. + final AttributeValues aAttributeValues = maAttributeManager.ParseAttributes( + mnCurrentStateId, + aAttributes); + + // d) creating a new ElementContext for the element that just starts. + maElementContextStack.push(maCurrentElementContext); + final ElementContext aPreviousElementContext = maCurrentElementContext; + maCurrentElementContext = new ElementContext( + sElementName, + maStateNameMap.GetNameForId(aTransition.GetActionId()), + false, + aAttributeValues, + aPreviousElementContext); + + // e) and run all actions that are bound to the the current start tag. + ExecuteActions( + mnCurrentStateId, + maCurrentElementContext, + ActionTrigger.ElementStart, + null, + aStartLocation, + aEndLocation); + + bResult = true; + } + } + catch (RuntimeException aException) + { + Log.Err.printf("error at line %d and column %d\n", + aStartLocation.getLineNumber(), + aStartLocation.getColumnNumber()); + throw aException; + } + return bResult; + } + + + + + public void ProcessEndElement ( + final String sNamespaceURI, + final String sElementName, + final Location aStartLocation, + final Location aEndLocation) + { + if ( ! maAcceptingStates.Contains(mnCurrentStateId) + && mnCurrentStateId!=-1) + { + if (Log.Dbg != null) + Log.Dbg.printf("current state %s(%d) is not an accepting state\n", + maStateNameMap.GetNameForId(mnCurrentStateId), + mnCurrentStateId); + throw new RuntimeException("not expecting end element "+sElementName); + } + + final NamespaceMap.NamespaceDescriptor aDescriptor = maNamespaceMap.GetDescriptorForURI(sNamespaceURI); + + // Leave the current element. + + final int nPreviousStateId = mnCurrentStateId; + mnCurrentStateId = maStateStack.pop(); + if (mnCurrentStateId == mnEndStateId) + mnCurrentStateId = mnStartStateId; + + final ElementContext aPreviousElementContext = maCurrentElementContext; + maCurrentElementContext = maElementContextStack.pop(); + + ExecuteActions( + nPreviousStateId, + aPreviousElementContext, + ActionTrigger.ElementEnd, + null, + aStartLocation, + aEndLocation); + + if (Log.Dbg != null) + { + Log.Dbg.DecreaseIndentation(); + Log.Dbg.printf("/%s:%s L%d%d\n", + aDescriptor.Prefix, + sElementName, + aStartLocation.getLineNumber(), + aStartLocation.getColumnNumber()); + Log.Dbg.printf(" %s(%d) <- %s(%d)\n", + maStateNameMap.GetNameForId(nPreviousStateId), + nPreviousStateId, + maStateNameMap.GetNameForId(mnCurrentStateId), + mnCurrentStateId); + } + } + + + + + public void ProcessCharacters ( + final String sText, + final Location aStartLocation, + final Location aEndLocation) + { + if (Log.Dbg != null) + Log.Dbg.printf("text [%s]\n", sText.replace("\n", "\\n")); + + ExecuteActions( + mnCurrentStateId, + maCurrentElementContext, + ActionTrigger.Text, + sText, + aStartLocation, + aEndLocation); + + } + + + + + public boolean IsInSkipState () + { + return maSkipStates.Contains(mnCurrentStateId); + } + + + + + public ActionManager GetActionManager () + { + return maActionManager; + } + + + + + private void ExecuteActions ( + final int nStateId, + final ElementContext aElementContext, + final ActionTrigger eTrigger, + final String sText, + final Location aStartLocation, + final Location aEndLocation) + { + final Iterable<IAction> aActions = maActionManager.GetActions(nStateId, eTrigger); + if (aActions != null) + for (final IAction aAction : aActions) + aAction.Run(eTrigger, aElementContext, sText, aStartLocation, aEndLocation); + } + + + + + private final NamespaceMap maNamespaceMap; + private final NameMap maNameMap; + private final NameMap maStateNameMap; + private final TransitionTable maTransitions; + private final SimpleTypeManager maSimpleTypeManager; + private final AttributeManager maAttributeManager; + private final NameMap maAttributeValueMap; + private int mnCurrentStateId; + private Stack<Integer> maStateStack; + private ElementContext maCurrentElementContext; + private Stack<ElementContext> maElementContextStack; + private final int mnStartStateId; + private final int mnEndStateId; + private SkipStateTable maSkipStates; + private AcceptingStateTable maAcceptingStates; + private final ActionManager maActionManager; + private final Vector<String> maErrorsAndWarnings; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/Transition.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/Transition.java new file mode 100644 index 000000000000..c5c956116b28 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/Transition.java @@ -0,0 +1,77 @@ +/************************************************************** +* +* 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.parser; + +class Transition +{ + Transition ( + final int nStartStateId, + final int nEndStateId, + final int nElementId, + final int nActionStateId) + { + mnStartStateId = nStartStateId; + mnEndStateId = nEndStateId; + mnElementId = nElementId; + mnActionStateId = nActionStateId; + } + + + + + public int GetStartStateId () + { + return mnStartStateId; + } + + + + + public int GetEndStateId () + { + return mnEndStateId; + } + + + + + public int GetElementId () + { + return mnElementId; + } + + + + + public int GetActionId () + { + return mnActionStateId; + } + + + + + private final int mnStartStateId; + private final int mnEndStateId; + private final int mnElementId; + private final int mnActionStateId; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/TransitionTable.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/TransitionTable.java new file mode 100644 index 000000000000..32326daa5a9f --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/TransitionTable.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.parser; + +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +public class TransitionTable +{ + public TransitionTable (final Vector<String[]> aData) + { + maTransitions = new HashMap<>(); + + for (final String[] aLine : aData) + { + // Create new transition. + final int nStartStateId = Integer.parseInt(aLine[1]); + final int nEndStateId = Integer.parseInt(aLine[2]); + final int nElementPrefixId = Integer.parseInt(aLine[3]); + final int nElementLocalId = Integer.parseInt(aLine[4]); + final int nElementStateId = Integer.parseInt(aLine[5]); + final Transition aTransition = new Transition( + nStartStateId, + nEndStateId, + (nElementPrefixId<<16) | nElementLocalId, + nElementStateId); + + Map<Integer,Transition> aPerElementTransitions = maTransitions.get(aTransition.GetStartStateId()); + if (aPerElementTransitions == null) + { + aPerElementTransitions = new HashMap<>(); + maTransitions.put(aTransition.GetStartStateId(), aPerElementTransitions); + } + aPerElementTransitions.put(aTransition.GetElementId(), aTransition); + } + } + + + + + public Transition GetTransition ( + final int nStateId, + final int nPrefixId, + final int nLocalId) + { + Map<Integer,Transition> aPerElementTransitions = maTransitions.get(nStateId); + if (aPerElementTransitions == null) + return null; + else + return aPerElementTransitions.get((nPrefixId<<16) | nLocalId); + } + + + + + public int GetTransitionCount () + { + return maTransitions.size(); + } + + + + + private final Map<Integer,Map<Integer,Transition>> maTransitions; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionDescriptor.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionDescriptor.java new file mode 100644 index 000000000000..27ce9d4b8f90 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionDescriptor.java @@ -0,0 +1,114 @@ +/************************************************************** +* +* 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.parser.action; + +import java.util.Vector; + +/** Container of all actions that are associated with a single state. + */ +public class ActionDescriptor +{ + public ActionDescriptor ( + final int nStateId, + final String sName) + { + msStateName = sName; + + maElementStartActions = null; + maElementEndActions = null; + maTextActions = null; + } + + + + + public void AddAction ( + final IAction aAction, + final ActionTrigger eTrigger) + { + GetActionsForTrigger(eTrigger, true).add(aAction); + } + + + + + public Iterable<IAction> GetActions ( + final ActionTrigger eTrigger) + { + return GetActionsForTrigger(eTrigger, false); + } + + + + + @Override + public String toString () + { + return "actions for state "+msStateName; + } + + + + + private Vector<IAction> GetActionsForTrigger ( + final ActionTrigger eTrigger, + final boolean bCreateWhenMissing) + { + Vector<IAction> aActions = null; + switch(eTrigger) + { + case ElementStart: + aActions = maElementStartActions; + if (bCreateWhenMissing && aActions==null) + { + aActions = new Vector<>(); + maElementStartActions = aActions; + } + break; + case ElementEnd: + aActions = maElementEndActions; + if (bCreateWhenMissing && aActions==null) + { + aActions = new Vector<>(); + maElementEndActions = aActions; + } + break; + case Text: + aActions = maTextActions; + if (bCreateWhenMissing && aActions==null) + { + aActions = new Vector<>(); + maTextActions = aActions; + } + break; + } + return aActions; + } + + + + + private final String msStateName; + private Vector<IAction> maElementStartActions; + private Vector<IAction> maElementEndActions; + private Vector<IAction> maTextActions; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionIterator.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionIterator.java new file mode 100644 index 000000000000..0ca176ad51bb --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionIterator.java @@ -0,0 +1,118 @@ +/************************************************************** +* +* 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.parser.action; + +import java.util.Iterator; + +/** Iterate over two sources of actions, both given as an Iterable<IAction> + * object that can be null. +*/ +public class ActionIterator implements Iterable<IAction> +{ + public ActionIterator ( + final Iterable<IAction> aOneStateActions, + final Iterable<IAction> aAllStateActions) + { + maOneStateActions = aOneStateActions; + maAllStateActions = aAllStateActions; + } + + + + + @Override public Iterator<IAction> iterator() + { + return new Iterator<IAction>() + { + Iterator<IAction> maIterator = null; + int mnPhase = 0; + + @Override + public boolean hasNext() + { + while(true) + { + if (mnPhase == 2) + return false; + else if (mnPhase == 0) + { + if (maIterator == null) + if (maOneStateActions == null) + { + mnPhase = 1; + continue; + } + else + maIterator = maOneStateActions.iterator(); + if (maIterator.hasNext()) + return true; + else + { + maIterator = null; + mnPhase = 1; + } + } + else if (mnPhase == 1) + { + if (maIterator == null) + if (maAllStateActions == null) + { + mnPhase = 2; + return false; + } + else + maIterator = maAllStateActions.iterator(); + if (maIterator.hasNext()) + return true; + else + { + mnPhase = 2; + } + } + } + } + + + + + @Override + public IAction next() + { + return maIterator.next(); + } + + + + + @Override + public void remove() + { + } + }; + } + + + + + private final Iterable<IAction> maOneStateActions; + private final Iterable<IAction> maAllStateActions; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionManager.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionManager.java new file mode 100644 index 000000000000..48d78a03977b --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionManager.java @@ -0,0 +1,165 @@ +/************************************************************** +* +* 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.parser.action; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.openoffice.ooxml.parser.NameMap; + +/** Manage actions that are bound to states and XML events. + */ +public class ActionManager +{ + public ActionManager ( + final NameMap aStateNameToIdMap) + { + maStateNameToIdMap = aStateNameToIdMap; + maAllStatesActions = new ActionDescriptor(0,"*"); + maStateToActionsMap = new HashMap<>(); + } + + + + + /** Add an action for an element start. + * @param sStateSelector + * The element is specified via a state name. This allows one element + * that leads to different complex types to have different actions, + * depending on the complex type. + * The selector value can be a full state name (including the namespace + * prefix and CT prefix, e.g. w06_CT_Table) or a regular expression + * (e.g. .*_CT_Table to match w06_CT_Table and w12_CT_Table). + * The action is bound to all matching states. + * @param aAction + * The action to call on entering any of the states that match the + * selector. + */ + public void AddElementStartAction ( + final String sStateSelector, + final IAction aAction) + { + AddAction(sStateSelector, aAction, ActionTrigger.ElementStart); + } + + + + + /** Add an action for an element end. + * @see AddElementStartAction. + */ + public void AddElementEndAction ( + final String sStateSelector, + final IAction aAction) + { + AddAction(sStateSelector, aAction, ActionTrigger.ElementEnd); + } + + + + + /** Add an action for XML text events. + * @see AddElementStartAction. + */ + public void AddTextAction ( + final String sStateSelector, + final IAction aAction) + { + AddAction(sStateSelector, aAction, ActionTrigger.Text); + } + + + + + /** Return an iterable object that gives access to all actions + * bound to the given state and trigger. + * Return value can be null when there are no actions bound to the state + * and trigger. + */ + public Iterable<IAction> GetActions ( + final int nStateId, + final ActionTrigger eTrigger) + { + final ActionDescriptor aOneStateActionsDescriptor = maStateToActionsMap.get(nStateId); + final Iterable<IAction> aOneStateActions = aOneStateActionsDescriptor!=null + ? aOneStateActionsDescriptor.GetActions(eTrigger) + : null; + final Iterable<IAction> aAllStateActions = maAllStatesActions.GetActions(eTrigger); + + if (aOneStateActions == null) + return aAllStateActions; + else if (aAllStateActions == null) + return aOneStateActions; + else + return new ActionIterator(aOneStateActions, aAllStateActions); + } + + + + + private void AddAction ( + final String sStateSelector, + final IAction aAction, + final ActionTrigger eTrigger) + { + if (sStateSelector.equals("*")) + { + // Simple optimization when an action is defined for all states. + maAllStatesActions.AddAction(aAction, eTrigger); + } + else if (sStateSelector.contains("*") || sStateSelector.contains("?")) + { + // The state selector contains wildcards. We have to iterate over + // all state names to find the matching ones. + for (final int nStateId : maStateNameToIdMap.GetMatchingStateIds(sStateSelector)) + { + GetActionDescriptor(nStateId).AddAction(aAction, eTrigger); + } + } + else + { + final int nStateId = maStateNameToIdMap.GetIdForName(sStateSelector); + GetActionDescriptor(nStateId).AddAction(aAction, eTrigger); + } + } + + + + + private ActionDescriptor GetActionDescriptor (final int nStateId) + { + ActionDescriptor aDescriptor = maStateToActionsMap.get(nStateId); + if (aDescriptor == null) + { + aDescriptor = new ActionDescriptor(nStateId, maStateNameToIdMap.GetNameForId(nStateId)); + maStateToActionsMap.put(nStateId, aDescriptor); + } + return aDescriptor; + } + + + + + private final NameMap maStateNameToIdMap; + private final ActionDescriptor maAllStatesActions; + private final Map<Integer,ActionDescriptor> maStateToActionsMap; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionTrigger.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionTrigger.java new file mode 100644 index 000000000000..33b781a0b1a6 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/ActionTrigger.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.parser.action; + +/** An enumeration of all supported action triggers. + */ +public enum ActionTrigger +{ + ElementStart, + ElementEnd, + Text +}
\ No newline at end of file diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/IAction.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/IAction.java new file mode 100644 index 000000000000..e784ed7c5537 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/action/IAction.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.parser.action; + +import javax.xml.stream.Location; + +import org.apache.openoffice.ooxml.parser.ElementContext; + +/** Interface for actions that are bound to states and triggered by XML events. + */ +public interface IAction +{ + /** Callback for a single XML event. + * @param eTrigger + * Equivalent to the XML event type. + * @param aContext + * The context of the element that was just entered (element start), + * is about to be left (element end) or is currently active (all other + * events). + * @param sText + * Contains text for ActionTrigger.Text. Is null for all other + * triggers. + * @param aStartLocation + * The location in the source file where the triggering element starts. + * @param aEndLocation + * The location in the source file where the triggering element ends. + */ + void Run ( + final ActionTrigger eTrigger, + final ElementContext aContext, + final String sText, + final Location aStartLocation, + final Location aEndLocation); +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeDescriptor.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeDescriptor.java new file mode 100644 index 000000000000..a51aa54f184f --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeDescriptor.java @@ -0,0 +1,130 @@ +/************************************************************** +* +* 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.parser.attribute; + +/** Store information about a single attribute (per state) that was read + * from the parse table. + * + * Note that an attribute that is defined for more than one state has one + * AttributeDescriptor object per state. + * + */ +public class AttributeDescriptor +{ + public AttributeDescriptor ( + final int nPrefixId, + final int nAttributeId, + final boolean bCanBeUnqualified, + final boolean bIsOptional, + final String sDefaultValue, + final String sAttributeName, + final int nAttributeTypeId) + { + mnNamespaceId = nPrefixId; + mnAttributeId = nAttributeId; + mbCanBeUnqualified = bCanBeUnqualified; + mbIsOptional = bIsOptional; + msDefaultValue = sDefaultValue; + msAttributeName = sAttributeName; + mnAttributeTypeId = nAttributeTypeId; + } + + + + + public int GetTypeId() + { + return mnAttributeTypeId; + } + + + + + public int GetNamespaceId () + { + return mnNamespaceId; + } + + + + + public int GetNameId () + { + return mnAttributeId; + } + + + + + public boolean CanBeUnqualified () + { + return mbCanBeUnqualified; + } + + + + + public boolean IsOptional () + { + return mbIsOptional; + } + + + + + public String GetDefaultValue () + { + return msDefaultValue; + } + + + + + public String GetName () + { + return msAttributeName; + } + + + + + @Override + public String toString () + { + return String.format( + "attribute %s(%d) of type %d", + msAttributeName, + mnAttributeId, + mnAttributeTypeId); + } + + + + + private final int mnNamespaceId; + private final int mnAttributeId; + private final boolean mbCanBeUnqualified; + private final boolean mbIsOptional; + private final String msDefaultValue; + private final String msAttributeName; + private final int mnAttributeTypeId; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeManager.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeManager.java new file mode 100644 index 000000000000..55b1df9f1f5e --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeManager.java @@ -0,0 +1,273 @@ +/************************************************************** +* +* 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.parser.attribute; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +import org.apache.openoffice.ooxml.parser.Log; +import org.apache.openoffice.ooxml.parser.NameMap; +import org.apache.openoffice.ooxml.parser.NamespaceMap; +import org.apache.openoffice.ooxml.parser.type.SimpleTypeManager; + + +/** Match a set of attributes from the document with the attribute + * specifications of a state. + * + */ +public class AttributeManager +{ + /** Create a new AttributeManager for the attribute specifications that + * are given in the parse table. + */ + public AttributeManager ( + final Vector<String[]> aData, + final NamespaceMap aNamespaceMap, + final NameMap aNameMap, + final NameMap aStateNameMap, + final SimpleTypeManager aSimpleTypeManager, + final Vector<String> aErrorsAndWarnings) + { + maStateIdToAttributesMap = new HashMap<>(); + maNamespaceMap = aNamespaceMap; + maNameMap = aNameMap; + maStateNameMap = aStateNameMap; + maSimpleTypeManager = aSimpleTypeManager; + maErrorsAndWarnings = aErrorsAndWarnings; + ParseData(aData); + } + + + + + private void ParseData (final Vector<String[]> aData) + { + for (final String[] aLine : aData) + { + final int nStateId = Integer.parseInt(aLine[1]); + final int nPrefixId = Integer.parseInt(aLine[2]); + final boolean bCanBeUnqualified = aLine[3].startsWith("u"); + final int nAttributeId = Integer.parseInt(aLine[4]); + final int nAttributeTypeId = aLine[5].equals("null") ? -1 : Integer.parseInt(aLine[5]); + final boolean bIsOptional = aLine[6].startsWith("o"); + final String sDefault = aLine[7]; + // State name. + final String sAttributeName = aLine[9]; + // Attribute type name. + + Map<Integer,AttributeDescriptor> aAttributesPerState = maStateIdToAttributesMap.get(nStateId); + if (aAttributesPerState == null) + { + aAttributesPerState = new HashMap<>(); + maStateIdToAttributesMap.put(nStateId, aAttributesPerState); + } + + final AttributeDescriptor aAttributeDescriptor = new AttributeDescriptor( + nPrefixId, + nAttributeId, + bCanBeUnqualified, + bIsOptional, + sDefault, + sAttributeName, + nAttributeTypeId); + + aAttributesPerState.put( + (nPrefixId<<16)|nAttributeId, + aAttributeDescriptor); + if (bCanBeUnqualified) + aAttributesPerState.put( + nAttributeId, + aAttributeDescriptor); + } + } + + + + + /** For the state with id nStateId, match the attributes from the document + * with the attribute specifications of that state. + */ + public AttributeValues ParseAttributes ( + final int nStateId, + final AttributeProvider aDocumentAttributes) + { + final AttributeValues aValues = new AttributeValues(); + + final Map<Integer,AttributeDescriptor> aAttributesPerState = maStateIdToAttributesMap.get(nStateId); + if (aAttributesPerState == null) + { + if (aDocumentAttributes.HasAttributes()) + { + Log.Std.printf("state has not attributes defined but document provides %d attributes\n", + aDocumentAttributes.GetAttributeCount()); + for (final String[] aEntry : aDocumentAttributes) + { + Log.Dbg.printf(" %s -> %s\n", aEntry[0], aEntry[1]); + } + throw new RuntimeException(); + } + } + else + { + final Set<AttributeDescriptor> aUsedAttributes = new HashSet<>(); + + // Process all attributes from the document. + for (final String[] aEntry : aDocumentAttributes) + { + final String sRawValue = aEntry[2]; + final AttributeDescriptor aAttributeDescriptor = ProcessAttribute( + aEntry[0], + aEntry[1], + sRawValue, + aAttributesPerState); + aUsedAttributes.add(aAttributeDescriptor); + final Object aProcessedValue = maSimpleTypeManager.PreprocessValue( + sRawValue, + aAttributeDescriptor); + if (aProcessedValue == null) + { + maSimpleTypeManager.PreprocessValue( + sRawValue, + aAttributeDescriptor); + throw new RuntimeException( + String.format("value '%s' of attribute '%s' is not recognized", + sRawValue, + aAttributeDescriptor.GetName())); + } + aValues.AddAttribute( + aAttributeDescriptor, + sRawValue, + aProcessedValue); + + if (Log.Dbg != null) + { + if (aAttributeDescriptor == null) + Log.Dbg.printf("attribute %s%s is not known\n", + aEntry[0]==null ? "" : ":"+aEntry[0], + aEntry[1]); + else + Log.Dbg.printf("attribute %s:%s(%d:%d) has type %s(%d) and value %s('%s')\n", + maNamespaceMap.GetDescriptorForId(aAttributeDescriptor.GetNamespaceId()).Prefix, + maNameMap.GetNameForId(aAttributeDescriptor.GetNameId()), + aAttributeDescriptor.GetNamespaceId(), + aAttributeDescriptor.GetNameId(), + maStateNameMap.GetNameForId(aAttributeDescriptor.GetTypeId()), + aAttributeDescriptor.GetTypeId(), + aProcessedValue, + sRawValue); + } + } + + // Check if all required attributes where given. + for (final AttributeDescriptor aAttribute : aAttributesPerState.values()) + { + if ( ! aUsedAttributes.contains(aAttribute)) + { + if ( ! aAttribute.IsOptional()) + { + final String sMessage = String.format("attribute '"+aAttribute.GetName()+"' is not present but also not optional"); + if (maErrorsAndWarnings != null) + maErrorsAndWarnings.add(sMessage); + else + throw new RuntimeException(sMessage); + } + else + { + // Add an entry that gives access to the default value. + aValues.AddAttribute(aAttribute, null, null); + } + } + } + } + + return aValues; + } + + + + + private AttributeDescriptor ProcessAttribute ( + final String sNamespace, + final String sAttributeName, + final String sAttributeValue, + final Map<Integer,AttributeDescriptor> aAttributesPerState) + { + final AttributeDescriptor aAttributeDescriptor; + if (sNamespace == null) + { + // Attribute name has no namespace. + final int nAttributeNameId = maNameMap.GetIdForName(sAttributeName); + aAttributeDescriptor = aAttributesPerState.get(nAttributeNameId); + } + else + { + // Attribute name has explicit namespace. + final NamespaceMap.NamespaceDescriptor aDescriptor = maNamespaceMap.GetDescriptorForURI(sNamespace); + final int nAttributeNameId = maNameMap.GetIdForName(sAttributeName); + aAttributeDescriptor = aAttributesPerState.get((aDescriptor.Id<<16) | nAttributeNameId); + } + return aAttributeDescriptor; + } + + + + + /** Remove the quotes around the given string. + * If it has the special value null (without quotes) then the null reference + * is returned. + */ + private String UnquoteString (final String sValue) + { + if (sValue.equals("null")) + return null; + else + { + assert(sValue.startsWith("\"")); + assert(sValue.endsWith("\"")); + return sValue.substring(1, sValue.length()-1); + } + } + + + + + public int GetAttributeCount () + { + int nCount = 0; + for (final Map<Integer,AttributeDescriptor> aMap : maStateIdToAttributesMap.values()) + nCount += aMap.size(); + return nCount; + } + + + + + private final Map<Integer,Map<Integer,AttributeDescriptor>> maStateIdToAttributesMap; + private final NamespaceMap maNamespaceMap; + private final NameMap maNameMap; + private final NameMap maStateNameMap; + private final SimpleTypeManager maSimpleTypeManager; + private final Vector<String> maErrorsAndWarnings; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeProvider.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeProvider.java new file mode 100644 index 000000000000..ab9e28550bc2 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeProvider.java @@ -0,0 +1,99 @@ +/************************************************************** +* +* 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.parser.attribute; + +import java.util.Iterator; + +import javax.xml.stream.XMLStreamReader; + +/** Give access to the attributes that are read from an OOXML stream. + */ +public class AttributeProvider + implements Iterable<String[]> +{ + public AttributeProvider (final XMLStreamReader aReader) + { + maReader = aReader; + } + + + + public boolean HasAttributes () + { + return maReader.getAttributeCount() > 0; + } + + + + + public String GetValue (final String sKey) + { + return maReader.getAttributeValue(null, sKey); + } + + + + @Override + public Iterator<String[]> iterator () + { + return new Iterator<String[]> () + { + int nIndex = 0; + final int nCount = maReader.getAttributeCount(); + + @Override public boolean hasNext() + { + return nIndex < nCount; + } + + @Override public String[] next() + { + final String[] aResult = new String[] + { + maReader.getAttributeNamespace(nIndex), + maReader.getAttributeLocalName(nIndex), + maReader.getAttributeValue(nIndex) + }; + ++nIndex; + return aResult; + } + + @Override public void remove() + { + } + + }; + } + + + + + public Integer GetAttributeCount () + { + return maReader.getAttributeCount(); + } + + + + + private final XMLStreamReader maReader; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeValues.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeValues.java new file mode 100644 index 000000000000..a419abbec0ec --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/attribute/AttributeValues.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.parser.attribute; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; + +/** Container of attribute values of an opening tag. + */ +public class AttributeValues +{ + AttributeValues () + { + maRawAttributeValues = new TreeMap<>(); + maProcessedAttributeValues = new TreeMap<>(); + } + + + + + public void AddAttribute ( + final AttributeDescriptor aAttributeDescriptor, + final String sRawValue, + final Object aProcessedValue) + { + maRawAttributeValues.put( + aAttributeDescriptor.GetName(), + sRawValue); + maProcessedAttributeValues.put( + aAttributeDescriptor.GetName(), + aProcessedValue); + } + + + + + public Iterable<Entry<String,String>> GetAttributes () + { + return maRawAttributeValues.entrySet(); + } + + + + + public String GetRawAttributeValue (final String sName) + { + return maRawAttributeValues.get(sName); + } + + + + + public Object GetProcessedAttributeValue (final String sName) + { + return maProcessedAttributeValues.get(sName); + } + + + + + public int GetAttributeCount () + { + return maRawAttributeValues.size(); + } + + + + + private Map<String,String> maRawAttributeValues; + private Map<String,Object> maProcessedAttributeValues; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/BlobParser.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/BlobParser.java new file mode 100644 index 000000000000..7edafaeb0b24 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/BlobParser.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.parser.type; + +import org.apache.openoffice.ooxml.parser.NameMap; + +public class BlobParser implements ISimpleTypeParser +{ + public BlobParser(final String[] aLine) + { + switch(aLine[5]) + { + case "B": + meType = Type.Base64Binary; + break; + case "H": + meType = Type.HexBinary; + break; + default: + throw new RuntimeException(); + } + switch(aLine[6]) + { + case "L": + mnLengthRestriction = Integer.parseInt(aLine[7]); + break; + case "N": + mnLengthRestriction = null; + break; + default: + throw new RuntimeException(); + } + } + + + + + @Override + public Object Parse ( + final String sRawValue, + final NameMap aAttributeValueMap) + { + if (mnLengthRestriction != null) + if (sRawValue.length()/2 != mnLengthRestriction) + return null; + /* + throw new RuntimeException( + String.format( + "length restriction (=%d) is violated, actual length is %d", + mnLengthRestriction, + sRawValue.length())); + */ + switch(meType) + { + case Base64Binary: + throw new RuntimeException("not yet implemented"); + + case HexBinary: + try + { + return Integer.parseInt(sRawValue, 16); + } + catch (NumberFormatException aException) + { + return null; + } + + default: + throw new RuntimeException(); + } + } + + + + + enum Type + { + Base64Binary, + HexBinary + }; + private final Type meType; + private final Integer mnLengthRestriction; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/DateTimeParser.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/DateTimeParser.java new file mode 100644 index 000000000000..979d4051b6e5 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/DateTimeParser.java @@ -0,0 +1,42 @@ +/************************************************************** +* +* 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.parser.type; + +import org.apache.openoffice.ooxml.parser.NameMap; + +public class DateTimeParser implements ISimpleTypeParser +{ + + public DateTimeParser(String[] aLine) + { + // TODO Auto-generated constructor stub + } + + @Override + public Object Parse( + final String sRawValue, + final NameMap aAttributeValueMap) + { + throw new RuntimeException(); + } + +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/ISimpleTypeParser.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/ISimpleTypeParser.java new file mode 100644 index 000000000000..aaa234009767 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/ISimpleTypeParser.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.parser.type; + +import org.apache.openoffice.ooxml.parser.NameMap; + +public interface ISimpleTypeParser +{ + Object Parse ( + final String sRawValue, + final NameMap aAttributeValueMap); +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/ListParser.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/ListParser.java new file mode 100644 index 000000000000..94feeb04e3b2 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/ListParser.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.parser.type; + +import org.apache.openoffice.ooxml.parser.NameMap; + +public class ListParser implements ISimpleTypeParser +{ + + public ListParser (final ISimpleTypeParser aItemParser) + { + maItemParser = aItemParser; + } + + + + + @Override + public Object Parse( + final String sRawValue, + final NameMap aAttributeValueMap) + { + final String[] aParts = sRawValue.split("\\s+"); + final Object[] aValues = new Object[aParts.length]; + for (int nIndex=0; nIndex<aParts.length; ++nIndex) + aValues[nIndex] = maItemParser.Parse(aParts[nIndex], aAttributeValueMap); + return aValues; + } + + + + + private final ISimpleTypeParser maItemParser; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/NumberParser.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/NumberParser.java new file mode 100644 index 000000000000..19fc833867cf --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/NumberParser.java @@ -0,0 +1,270 @@ +/************************************************************** +* +* 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.parser.type; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.openoffice.ooxml.parser.NameMap; + +public class NumberParser implements ISimpleTypeParser +{ + public NumberParser (final String[] aLine) + { + switch(aLine[5]) + { + case "u1": + meNumberType = NumberType.Boolean; + meJavaNumberType = JavaNumberType.Boolean; + break; + case "s8": + meNumberType = NumberType.Byte; + meJavaNumberType = JavaNumberType.Byte; + break; + case "u8": + meNumberType = NumberType.UnsignedByte; + meJavaNumberType = JavaNumberType.Short; + break; + case "s16": + meNumberType = NumberType.Short; + meJavaNumberType = JavaNumberType.Short; + break; + case "u16": + meNumberType = NumberType.UnsignedShort; + meJavaNumberType = JavaNumberType.Integer; + break; + case "s32": + meNumberType = NumberType.Int; + meJavaNumberType = JavaNumberType.Integer; + break; + case "u32": + meNumberType = NumberType.UnsignedInt; + meJavaNumberType = JavaNumberType.Long; + break; + case "s64": + meNumberType = NumberType.Long; + meJavaNumberType = JavaNumberType.Long; + break; + case "u64": + meNumberType = NumberType.UnsignedLong; + meJavaNumberType = JavaNumberType.Long; + break; + case "s*": + meNumberType = NumberType.Integer; + meJavaNumberType = JavaNumberType.Long; + break; + case "f": + meNumberType = NumberType.Float; + meJavaNumberType = JavaNumberType.Float; + break; + case "d": + meNumberType = NumberType.Double; + meJavaNumberType = JavaNumberType.Double; + break; + default: + throw new RuntimeException("unsupported numerical type "+aLine[5]); + } + + switch(aLine[6]) + { + case "E": + meRestrictionType = RestrictionType.Enumeration; + maEnumeration = new HashSet<>(); + for (int nIndex=7; nIndex<aLine.length; ++nIndex) + maEnumeration.add(ParseNumber(aLine[nIndex])); + break; + + case "S": + meRestrictionType = RestrictionType.Size; + for (int nIndex=7; nIndex<=9; nIndex+=2) + if (nIndex<aLine.length) + switch (aLine[nIndex]) + { + case "<=": + maMaximumValue = ParseNumber(aLine[nIndex+1]); + mbIsMaximumInclusive = true; + break; + case "<": + maMaximumValue = ParseNumber(aLine[nIndex+1]); + mbIsMaximumInclusive = false; + break; + case ">=": + maMinimumValue = ParseNumber(aLine[nIndex+1]); + mbIsMinimumInclusive = true; + break; + case ">": + maMinimumValue = ParseNumber(aLine[nIndex+1]); + mbIsMinimumInclusive = false; + break; + } + break; + + case "N": + meRestrictionType = RestrictionType.None; + break; + + default: + throw new RuntimeException("unsupported numerical restriction "+aLine[6]); + } + } + + + + + @Override + public Object Parse( + final String sRawValue, + final NameMap aAttributeValueMap) + { + final Object aNumber = ParseNumber(sRawValue); + switch(meRestrictionType) + { + case Enumeration: + if (maEnumeration.contains(aNumber)) + return aNumber; + else + return null; + + case Size: + if (maMinimumValue != null) + if (mbIsMinimumInclusive) + { + if (CompareTo(aNumber, maMinimumValue, meJavaNumberType) < 0) + return null; + } + else + { + if (CompareTo(aNumber, maMinimumValue, meJavaNumberType) <= 0) + return null; + } + if (maMaximumValue != null) + if (mbIsMaximumInclusive) + { + if (CompareTo(aNumber, maMaximumValue, meJavaNumberType) > 0) + return null; + } + else + { + if (CompareTo(aNumber, maMaximumValue, meJavaNumberType) >= 0) + return null; + } + return aNumber; + + case None: + return aNumber; + + default: + throw new RuntimeException(); + } + } + + + + + Object ParseNumber (final String sNumber) + { + switch(meJavaNumberType) + { + case Boolean: return Boolean.parseBoolean(sNumber); + case Byte: return Byte.parseByte(sNumber); + case Short: return Short.parseShort(sNumber); + case Integer: return Integer.parseInt(sNumber); + case Long: return Long.parseLong(sNumber); + case Float: return Float.parseFloat(sNumber); + case Double: return Double.parseDouble(sNumber); + default: + throw new RuntimeException(); + } + } + + + + + private static int CompareTo ( + final Object aLeft, + final Object aRight, + final JavaNumberType eType) + { + switch(eType) + { + case Boolean: + return ((Boolean)aLeft).compareTo((Boolean)aRight); + case Byte: + return ((Byte)aLeft).compareTo((Byte)aRight); + case Short: + return ((Short)aLeft).compareTo((Short)aRight); + case Integer: + return ((Integer)aLeft).compareTo((Integer)aRight); + case Long: + return ((Long)aLeft).compareTo((Long)aRight); + case Float: + return ((Float)aLeft).compareTo((Float)aRight); + case Double: + return ((Double)aLeft).compareTo((Double)aRight); + default: + throw new RuntimeException(); + } + } + + + + + enum NumberType + { + Boolean, + Byte, + UnsignedByte, + Short, + UnsignedShort, + Int, + UnsignedInt, + Long, + UnsignedLong, + Integer, + Float, + Double + } + enum JavaNumberType + { + Boolean, + Byte, + Short, + Integer, + Long, + Float, + Double + } + enum RestrictionType + { + Enumeration, + Size, + None + } + private final NumberType meNumberType; + private final JavaNumberType meJavaNumberType; + private final RestrictionType meRestrictionType; + private Set<Object> maEnumeration; + private Object maMinimumValue; + private boolean mbIsMinimumInclusive; + private Object maMaximumValue; + private boolean mbIsMaximumInclusive; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/SimpleTypeManager.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/SimpleTypeManager.java new file mode 100644 index 000000000000..abeb529ddc52 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/SimpleTypeManager.java @@ -0,0 +1,118 @@ +/************************************************************** +* +* 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.parser.type; + +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +import org.apache.openoffice.ooxml.parser.NameMap; +import org.apache.openoffice.ooxml.parser.attribute.AttributeDescriptor; + +public class SimpleTypeManager +{ + public SimpleTypeManager( + final Vector<String[]> aData, + final NameMap aAttributeValueMap) + { + maAttributeValueMap = aAttributeValueMap; + maSimpleTypeToParsersMap = new HashMap<>(); + ParseData(aData); + } + + + + + private void ParseData (final Vector<String[]> aData) + { + for (final String[] aLine : aData) + { + final int nSimpleTypeId = Integer.parseInt(aLine[1]); +// final int nVariant = Integer.parseInt(aLine[2]); + final boolean bIsList = aLine[3].equals("L"); + final ISimpleTypeParser aVariantParser; + switch (aLine[4]) + { + case "S": + aVariantParser = new StringParser(aLine); + break; + case "N": + aVariantParser = new NumberParser(aLine); + break; + case "D": + aVariantParser = new DateTimeParser(aLine); + break; + case "B": + aVariantParser = new BlobParser(aLine); + break; + default: + throw new RuntimeException("unexpected parser type: "+aLine[4]); + } + + Vector<ISimpleTypeParser> aVariants = maSimpleTypeToParsersMap.get(nSimpleTypeId); + if (aVariants == null) + { + aVariants = new Vector<>(); + maSimpleTypeToParsersMap.put(nSimpleTypeId, aVariants); + } + if (bIsList) + aVariants.add(new ListParser(aVariantParser)); + else + aVariants.add(aVariantParser); + } + } + + + + + public Object PreprocessValue ( + final String sRawValue, + final AttributeDescriptor aAttributeDescriptor) + { + final Vector<ISimpleTypeParser> aTypeParsers = maSimpleTypeToParsersMap.get(aAttributeDescriptor.GetTypeId()); + if (aTypeParsers == null) + throw new RuntimeException("type "+aAttributeDescriptor.GetTypeId()+" is not supported"); + + for (final ISimpleTypeParser aParser : aTypeParsers) + { + try + { + final Object aProcessedValue = aParser.Parse( + sRawValue, + maAttributeValueMap); + if (aProcessedValue != null) + return aProcessedValue; + } + catch(final Exception aException) + { + return "error"; + } + } + return null; + } + + + + + private final NameMap maAttributeValueMap; + private Map<Integer,Vector<ISimpleTypeParser>> maSimpleTypeToParsersMap; +} diff --git a/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/StringParser.java b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/StringParser.java new file mode 100644 index 000000000000..c3b22bf5fba0 --- /dev/null +++ b/ooxml/source/framework/JavaOOXMLParser/src/org/apache/openoffice/ooxml/parser/type/StringParser.java @@ -0,0 +1,122 @@ +/************************************************************** +* +* 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.parser.type; + +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.openoffice.ooxml.parser.NameMap; + +public class StringParser + implements ISimpleTypeParser +{ + + public StringParser (final String[] aLine) + { + switch(aLine[5]) + { + case "E": + meRestrictionType = RestrictionType.Enumeration; + maEnumeration = new HashSet<>(); + for (int nIndex=6; nIndex<aLine.length; ++nIndex) + maEnumeration.add(Integer.parseInt(aLine[nIndex])); + break; + + case "P": + meRestrictionType = RestrictionType.Pattern; + maPattern = Pattern.compile(aLine[6].replace("\\p{Is", "\\p{In")); + break; + + case "L": + meRestrictionType = RestrictionType.Length; + mnMinimumLength = Integer.parseInt(aLine[6]); + mnMaximumLength = Integer.parseInt(aLine[7]); + break; + + case "N": + meRestrictionType = RestrictionType.None; + break; + + default: + throw new RuntimeException(); + } + } + + + + + @Override + public Object Parse ( + final String sRawValue, + final NameMap aAttributeValueMap) + { + switch(meRestrictionType) + { + case Enumeration: + final int nId = aAttributeValueMap.GetIdForOptionalName(sRawValue); + if ( ! maEnumeration.contains(nId)) + return null;//throw new RuntimeException("value is not part of enumeration"); + else + return nId; + + case Pattern: + if ( ! maPattern.matcher(sRawValue).matches()) + return null;//throw new RuntimeException("value does not match pattern"); + else + return sRawValue; + + case Length: + if (sRawValue.length()<mnMinimumLength || sRawValue.length()>mnMaximumLength) + return null;/*throw new RuntimeException( + String.format("value violates string length restriction: %s is not inside [%d,%d]", + sRawValue.length(), + mnMinimumLength, + mnMaximumLength)); + */ + else + return sRawValue; + + case None: + return sRawValue; + + default: + throw new RuntimeException(); + } + } + + + + + enum RestrictionType + { + Enumeration, + Pattern, + Length, + None + } + private final RestrictionType meRestrictionType; + private Set<Integer> maEnumeration; + private Pattern maPattern; + private int mnMinimumLength; + private int mnMaximumLength; +} diff --git a/ooxml/source/framework/JavaPartManager/.classpath b/ooxml/source/framework/JavaPartManager/.classpath new file mode 100644 index 000000000000..5ec76483b264 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/.classpath @@ -0,0 +1,7 @@ +<?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 combineaccessrules="false" kind="src" path="/JavaOOXMLParser"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/ooxml/source/framework/JavaPartManager/.project b/ooxml/source/framework/JavaPartManager/.project new file mode 100644 index 000000000000..08a7d0d1b383 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>JavaPartManager</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/JavaPartManager/.settings/org.eclipse.jdt.core.prefs b/ooxml/source/framework/JavaPartManager/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000000..7341ab1683c4 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/.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/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/ContentType.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/ContentType.java new file mode 100644 index 000000000000..fc147cbd5cd4 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/ContentType.java @@ -0,0 +1,77 @@ +package org.apache.openoffice.ooxml.framework.part; + +public enum ContentType +{ + ApplicationDrawing ("application/vnd.openxmlformats-officedocument.vmlDrawing"), + ApplicationExcel ("application/vnd.ms-excel"), + ApplicationXML ("application/xml"), + Chart ("application/vnd.openxmlformats-officedocument.drawingml.chart+xml"), + ContentTypes (""), + CoreProperties ("application/vnd.openxmlformats-package.core-properties+xml"), + CustomXMLProperties ("application/vnd.openxmlformats-officedocument.customXmlProperties+xml"), + ExtendedProperties ("application/vnd.openxmlformats-officedocument.extended-properties+xml"), + ImageGIF ("image/gif"), + ImageJPG ("image/png"), + ImagePNG ("image/jpeg"), + OleObject ("application/vnd.openxmlformats-officedocument.oleObject"), + PmlDocument ("application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"), + PmlHandoutMaster ("application/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml"), + PmlNotesMaster ("application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml"), + PmlNotesSlide ("application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml"), + PmlProperties ("application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"), + PmlSlide ("application/vnd.openxmlformats-officedocument.presentationml.slide+xml"), + PmlSlideLayout ("application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"), + PmlSlideMaster ("application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"), + PmlTableStyles ("application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml"), + PmlViewProperties ("application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml"), + Relationships ("application/vnd.openxmlformats-package.relationships+xml"), + SmlSheet ("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), + Theme ("application/vnd.openxmlformats-officedocument.theme+xml"), + ThemeOverride ("application/vnd.openxmlformats-officedocument.themeOverride+xml"), + Thumbnail ("http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"), + WmlDocument ("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"), + WmlEndNotes ("application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml"), + WmlFontTable ("application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"), + WmlFootNotes ("application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml"), + WmlFooter ("application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"), + WmlHeader ("application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"), + WmlNumbering ("application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"), + WmlSettings ("application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"), + WmlStyles ("application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"), + WmlWebSettings ("application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"), + + Unknown(""); + + + + + ContentType (final String sMimeType) + { + msMimeType = sMimeType; + } + + + + + public static ContentType CreateForString (final String sContentType) + { + for (final ContentType eType : values()) + if (eType.msMimeType.equals(sContentType)) + return eType; + System.err.printf("content type '%s' is not known\n", sContentType); + return Unknown; + } + + + + + public String GetLongName () + { + return msMimeType; + } + + + + + private final String msMimeType; +} diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/ContentTypes.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/ContentTypes.java new file mode 100644 index 000000000000..d17a082106a8 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/ContentTypes.java @@ -0,0 +1,150 @@ +package org.apache.openoffice.ooxml.framework.part; + +import java.util.HashMap; +import java.util.Map; + +import javax.xml.stream.Location; + +import org.apache.openoffice.ooxml.framework.part.parser.ParserFactory; +import org.apache.openoffice.ooxml.parser.ElementContext; +import org.apache.openoffice.ooxml.parser.Parser; +import org.apache.openoffice.ooxml.parser.action.ActionTrigger; +import org.apache.openoffice.ooxml.parser.action.IAction; + +public class ContentTypes +{ + ContentTypes (final PartManager aPartManager) + { + maExtensionToContentTypeMap = new HashMap<>(); + maPartNameToContentTypeMap = new HashMap<>(); + + // Technically the [Content_Types].xml stream is not a part and + // "[Content_Types].xml" is not a part name. But handling it like one + // makes the implementation a little bit easier and more readable. + final Part aContentTypesPart = new Part( + ContentType.ContentTypes, + aPartManager, + new PartName("/[Content_Types].xml")); + + final Parser aParser = ParserFactory.getParser( + aContentTypesPart.getContentType(), + aContentTypesPart.getStream(), + null); + /* + DefineContext( + CT_Something, + int nValue, + CallbackObject aObject); + + class CT_Something_Context : public Context + { + Context parent + + attribute 1 + ... + attribute n + int nValue; + CallbackObject aObject; + } + + DefineElementStartAction( + CT_Something_Context, + aObject, + DoSomething); + + + case ElementStart of CT_Something: + maCurrentContext.aObject.DoSomething(maCurrentContext); // CT_Something_Context + + // + CallbackObject.cxx + + class CallbackObject + { + public: DoSomething(CT_Something_Context aContext) + { + aContext.attribute1 + } + } + + */ + aParser.GetActionManager().AddElementStartAction( + ".*_CT_Default", + new IAction(){ + + @Override + public void Run( + final ActionTrigger eTrigger, + final ElementContext aContext, + final String sText, + final Location aStartLocation, + final Location aEndLocation) + { + ProcessDefault( + aContext.GetAttributes().GetRawAttributeValue("A_Extension"), + aContext.GetAttributes().GetRawAttributeValue("A_ContentType")); + + }}); + aParser.GetActionManager().AddElementStartAction( + ".*_CT_Override", + new IAction(){ + + @Override + public void Run( + final ActionTrigger eTrigger, + final ElementContext aContext, + final String sText, + final Location aStartLocation, + final Location aEndLocation) + { + ProcessOverride( + aContext.GetAttributes().GetRawAttributeValue("A_PartName"), + aContext.GetAttributes().GetRawAttributeValue("A_ContentType")); + + }}); + + + aParser.Parse(); + } + + + + + public ContentType getTypeForPartName (final PartName aName) + { + ContentType eType = maPartNameToContentTypeMap.get(aName.GetFullname()); + if (eType == null) + eType = maExtensionToContentTypeMap.get(aName.GetExtension()); + if (eType == null) + eType = ContentType.Unknown; + return eType; + } + + + + + private void ProcessDefault ( + final String sExtension, + final String sContentTypeName) + { + final ContentType eType = ContentType.CreateForString(sContentTypeName); + maExtensionToContentTypeMap.put(sExtension, eType); + } + + + + + private void ProcessOverride ( + final String sPartName, + final String sContentTypeName) + { + final ContentType eType = ContentType.CreateForString(sContentTypeName); + maPartNameToContentTypeMap.put(sPartName, eType); + } + + + + + private final Map<String,ContentType> maExtensionToContentTypeMap; + private final Map<String,ContentType> maPartNameToContentTypeMap; +} diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/IReferenceProvider.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/IReferenceProvider.java new file mode 100644 index 000000000000..9d47eb6f54fe --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/IReferenceProvider.java @@ -0,0 +1,6 @@ +package org.apache.openoffice.ooxml.framework.part; + +public interface IReferenceProvider +{ + RelatedParts getRelatedParts (); +} diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/OOXMLPackage.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/OOXMLPackage.java new file mode 100644 index 000000000000..3e5310ada923 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/OOXMLPackage.java @@ -0,0 +1,56 @@ +package org.apache.openoffice.ooxml.framework.part; + +import java.io.File; +import java.io.InputStream; + +public class OOXMLPackage + extends Package +{ + public static OOXMLPackage Create (final File aOOXMLFile) + { + return new OOXMLPackage( + aOOXMLFile.getAbsolutePath(), + new PartManager(aOOXMLFile)); + } + + + + + private OOXMLPackage (final String sPath, final PartManager aPartManager) + { + super(sPath, aPartManager); + } + + + + + /** Return a list of stream names. + * Note that that list is not necessarily identical to the list of part + * names. It can contain entries that are not parts. + */ + public String[] listStreamNames () + { + return maPartManager.listStreamNames(); + } + + + + + /** Return an InputStream object for the specified stream. + */ + public InputStream getStream (final String sStreamName) + { + return maPartManager.getStreamForStreamName(sStreamName); + } + + + + + public Part getPart (final PartName aPartName) + { + return new Part ( + maPartManager.getContentTypes().getTypeForPartName(aPartName), + maPartManager, + aPartName); + } +}
\ No newline at end of file diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/Package.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/Package.java new file mode 100644 index 000000000000..dd31292cf135 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/Package.java @@ -0,0 +1,158 @@ +package org.apache.openoffice.ooxml.framework.part; + +import java.io.File; +import java.io.InputStream; +import java.lang.ref.SoftReference; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +public class Package + implements IReferenceProvider +{ + public static Package Create (final File aOOXMLFile) + { + return new Package( + aOOXMLFile.getAbsolutePath(), + new PartManager(aOOXMLFile)); + } + + + + + protected Package ( + final String sFullFilename, + final PartManager aPartManager) + { + msFullFilename = sFullFilename; + maPartManager = aPartManager; + maRelatedParts = new RelatedParts( + new PartName(""), + aPartManager); + + final PartName aDocumentPartName = maRelatedParts.GetSingleTargetForType(RelationshipType.OfficeDocument); + maOfficeDocumentPart = new Part( + maPartManager.getContentTypes().getTypeForPartName(aDocumentPartName), + maPartManager, + aDocumentPartName); + } + + + + + public Part getOfficeDocumentPart () + { + return maOfficeDocumentPart; + } + + + + + public Iterable<Part> getDigitalSignaturesParts () + { + // TODO + return new Vector<>(); + } + + + + + public RelatedParts getRelatedParts () + { + return maRelatedParts; + } + + + + + public boolean hasAppDefFilePropertiesPart () + { + // TODO + return false; + } + + + + + public Part getAppDefFilePropertiesPart () + { + // TODO + return null; + } + + + + + public boolean hasCoreFilePropertiesPart () + { + // TODO + return false; + } + + + + + public Part getCoreFilePropertiesPart () + { + // TODO + return null; + } + + + + + public boolean hasCustomFilePropertiesPart () + { + // TODO + return false; + } + + + + + public Part getCustomFilePropertiesPart () + { + // TODO + return null; + } + + + + + public String getFileName() + { + return msFullFilename; + } + + + + + /** Return a list of stream names. + * Note that that list is not necessarily identical to the list of part + * names. It can contain entries that are not parts. + */ + public String[] listStreamNames () + { + return maPartManager.listStreamNames(); + } + + + + + /** Return an InputStream object for the specified stream. + */ + public InputStream getStream (final String sStreamName) + { + return maPartManager.getStreamForStreamName(sStreamName); + } + + + + + private final String msFullFilename; + protected final PartManager maPartManager; + private final RelatedParts maRelatedParts; + private final Part maOfficeDocumentPart; +}
\ No newline at end of file diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/Part.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/Part.java new file mode 100644 index 000000000000..28b2617a1494 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/Part.java @@ -0,0 +1,89 @@ +package org.apache.openoffice.ooxml.framework.part; + +import java.io.InputStream; + +public class Part + implements IReferenceProvider +{ + public Part ( + final ContentType eType, + final PartManager aPartManager, + final PartName aPartName) + { + meContentType = eType; + maPartManager = aPartManager; + maPartName = aPartName; + maRelatedParts = null; + } + + + + + public Part getPartById (final String sId) + { + final PartName aName = getRelatedParts().GetTargetForId(sId); + return new Part( + maPartManager.getContentTypes().getTypeForPartName(aName), + maPartManager, + aName); + } + + + + + public Part getPartByRelationshipType (final RelationshipType eType) + { + final PartName aName = getRelatedParts().GetSingleTargetForType(eType); + return new Part( + maPartManager.getContentTypes().getTypeForPartName(aName), + maPartManager, + aName); + } + + + + + public PartName getPartName () + { + return maPartName; + } + + + + + public ContentType getContentType () + { + return meContentType; + } + + + + + public InputStream getStream() + { + return maPartManager.getStreamForPartName(maPartName); + } + + + + + @Override + public RelatedParts getRelatedParts () + { + if (maRelatedParts == null) + { + maRelatedParts = new RelatedParts( + maPartName, + maPartManager); + } + return maRelatedParts; + } + + + + + private final ContentType meContentType; + private final PartManager maPartManager; + private final PartName maPartName; + private RelatedParts maRelatedParts; +} diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/PartManager.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/PartManager.java new file mode 100644 index 000000000000..c61f16572487 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/PartManager.java @@ -0,0 +1,145 @@ +package org.apache.openoffice.ooxml.framework.part; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.SoftReference; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class PartManager +{ + public PartManager (final File aFile) + { + ZipFile aZipFile = null; + try + { + aZipFile = new ZipFile(aFile); + } + catch (Exception e) + { + e.printStackTrace(); + } + maZipFile = aZipFile; + maPartNameToPartMap = new HashMap<>(); + } + + + + + public InputStream getStreamForPartName (final PartName aPartName) + { + final ZipEntry aEntry = maZipFile.getEntry( + ToZipEntryName(aPartName.GetFullname())); + if (aEntry == null) + return null; + + try + { + return maZipFile.getInputStream(aEntry); + } + catch (IOException e) + { + e.printStackTrace(); + return null; + } + } + + + + + /** This is the low-level variant of getStreamForPartName(). + * It can return streams for entries in the OOXML zip package + * that are not, technically, parts. + * @return + * Returns null when the named stream does not exist or can not be + * opened. + */ + public InputStream getStreamForStreamName (final String sStreamName) + { + final ZipEntry aEntry = maZipFile.getEntry( + ToZipEntryName(sStreamName)); + try + { + return maZipFile.getInputStream(aEntry); + } + catch (IOException e) + { + e.printStackTrace(); + return null; + } + } + + + + + public ContentTypes getContentTypes () + { + if (maContentTypes == null) + { + maContentTypes = new ContentTypes(this); + } + return maContentTypes; + } + + + + + private final String ToZipEntryName (final String sPath) + { + return sPath.substring(1); + } + + + + + /** Return a list of the names of all streams. + * Note that that list is not necessarily identical to the list of part + * names. It can contain entries that are not parts. + */ + public final String[] listStreamNames () + { + final Vector<String> aStreamNames = new Vector<>(); + + final Enumeration<? extends ZipEntry> aEntries = maZipFile.entries(); + while (aEntries.hasMoreElements()) + { + final ZipEntry aEntry = aEntries.nextElement(); + aStreamNames.add(aEntry.getName()); + } + + return aStreamNames.toArray(new String[0]); + } + + + + + public Part getPart (final PartName aName) + { + SoftReference<Part> aSoftPart = maPartNameToPartMap.get(aName); + Part aPart = null; + if (aSoftPart != null) + aPart = aSoftPart.get(); + if (aPart == null) + { + aPart = new Part( + getContentTypes().getTypeForPartName(aName), + this, + aName); + maPartNameToPartMap.put(aName, new SoftReference<Part>(aPart)); + } + + return aPart; + } + + + + + private ZipFile maZipFile; + private ContentTypes maContentTypes; + private final Map<PartName, SoftReference<Part>> maPartNameToPartMap; +} diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/PartManagerPrototype.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/PartManagerPrototype.java new file mode 100644 index 000000000000..8aa820714d67 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/PartManagerPrototype.java @@ -0,0 +1,33 @@ +package org.apache.openoffice.ooxml.framework.part; + +import java.io.File; + +import org.apache.openoffice.ooxml.framework.part.parser.ParserFactory; +import org.apache.openoffice.ooxml.parser.Log; + +public class PartManagerPrototype +{ + public static void main (final String ... aArgumentList) + { + if (aArgumentList.length != 3) + { + System.err.printf("usage: PartManagerPrototype <ooxml-file-name> <parser-table-filename> <log-filename>"); + System.exit(1); + } + + final long nStartTime = System.currentTimeMillis(); + + Log.Dbg = new Log(aArgumentList[2]); + ParserFactory.SetParserTableFilename(aArgumentList[1]); + + final File aOOXMLFile = new File(aArgumentList[0]); + final Part aPart = OOXMLPackage.Create(aOOXMLFile).getOfficeDocumentPart().getPartById("rId1"); + + final long nEndTime = System.currentTimeMillis(); + + System.out.printf("got content type %s for %s in %fs\n", + aPart.getContentType(), + aPart.getPartName(), + (nEndTime-nStartTime)/1000.0); + } +} diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/PartName.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/PartName.java new file mode 100644 index 000000000000..e91aa37eac49 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/PartName.java @@ -0,0 +1,130 @@ +package org.apache.openoffice.ooxml.framework.part; + +/** Operations around part names. + */ +public class PartName + implements Comparable<PartName> +{ + public PartName (final String sPath) + { + if ( ! (sPath.isEmpty() || sPath.startsWith("/"))) + { + assert(sPath.isEmpty() || sPath.startsWith("/")); + } + assert(sPath.indexOf('\\') == -1); + + msPath = sPath; + } + + + + + public PartName ( + final String sPath, + final PartName aParentName, + final String sMode) + { + switch(sMode) + { + case "External": + msPath = sPath; + break; + + case "Internal": + msPath = Cleanup(aParentName.GetPathname() + "/" + sPath); + break; + + default: + throw new RuntimeException(); + } + } + + + + + public PartName getRelationshipsPartName () + { + return new PartName(GetPathname() + "/_rels/" + GetBasename() + ".rels"); + } + + + + + private String GetPathname () + { + if (msPath.isEmpty()) + return ""; + else + { + final int nPathnameEnd = msPath.lastIndexOf('/'); + assert(nPathnameEnd>=0); + return msPath.substring(0, nPathnameEnd); + } + } + + + + + public String GetBasename () + { + if (msPath.isEmpty()) + return ""; + else + { + final int nBasenameStart = msPath.lastIndexOf('/'); + assert(nBasenameStart>=0); + return msPath.substring(nBasenameStart+1); + } + } + + + + + public String GetExtension () + { + final int nExtensionStart = msPath.lastIndexOf('.'); + if (nExtensionStart < 0) + return null; + else + return msPath.substring(nExtensionStart+1); + } + + + + + public String GetFullname() + { + return msPath; + } + + + + + @Override + public int compareTo (final PartName aOther) + { + return msPath.compareTo(aOther.msPath); + } + + + + + private String Cleanup (final String sName) + { + return sName.replaceAll("/[^/]+/\\.\\./", "/"); + } + + + + + @Override + public String toString () + { + return msPath; + } + + + + + private final String msPath; +} diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/RelatedParts.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/RelatedParts.java new file mode 100644 index 000000000000..22cd218c9ab8 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/RelatedParts.java @@ -0,0 +1,131 @@ +package org.apache.openoffice.ooxml.framework.part; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; + +import javax.xml.stream.Location; + +import org.apache.openoffice.ooxml.framework.part.parser.ParserFactory; +import org.apache.openoffice.ooxml.parser.ElementContext; +import org.apache.openoffice.ooxml.parser.Parser; +import org.apache.openoffice.ooxml.parser.action.ActionTrigger; +import org.apache.openoffice.ooxml.parser.action.IAction; + +public class RelatedParts +{ + RelatedParts ( + final PartName aPartName, + final PartManager aPartManager) + { + maIdToTargetMap = new HashMap<>(); + maTypeToTargetsMap = new HashMap<>(); + + final InputStream aStream = aPartManager.getStreamForPartName(aPartName.getRelationshipsPartName()); + if (aStream != null) + { + final Parser aParser = ParserFactory.getParser( + ContentType.Relationships, + aStream, + null); + aParser.GetActionManager().AddElementStartAction( + "A_CT_Relationship", + new IAction() + { + @Override public void Run ( + final ActionTrigger eTrigger, + final ElementContext aContext, + final String sText, + final Location aStartLocation, + final Location aEndLocation) + { + final String sId = aContext.GetAttributes().GetRawAttributeValue("A_Id"); + final String sType = aContext.GetAttributes().GetRawAttributeValue("A_Type"); + final String sTarget = aContext.GetAttributes().GetRawAttributeValue("A_Target"); + String sTargetMode = aContext.GetAttributes().GetRawAttributeValue("A_TargetMode"); + if (sTargetMode == null) + sTargetMode = "Internal"; + + AddRelationship( + sId, + RelationshipType.CreateFromString(sType), + new PartName(sTarget, aPartName, sTargetMode)); + } + } + ); + aParser.Parse(); + } + } + + + + + private void AddRelationship ( + final String sId, + final RelationshipType eType, + final PartName aTarget) + { + maIdToTargetMap.put(sId, aTarget); + + Vector<PartName> aTargets = maTypeToTargetsMap.get(eType); + if (aTargets == null) + { + aTargets = new Vector<>(); + maTypeToTargetsMap.put(eType, aTargets); + } + aTargets.add(aTarget); + } + + + + + public PartName GetTargetForId (final String sId) + { + return maIdToTargetMap.get(sId); + } + + + + + public Iterable<PartName> GetTargetsForType (final RelationshipType eType) + { + return maTypeToTargetsMap.get(eType); + } + + + + public Iterable<PartName> getAllTargets () + { + final Set<PartName> aAllNames = new TreeSet<>(); + aAllNames.addAll(maIdToTargetMap.values()); + return aAllNames; + } + + + + + public PartName GetSingleTargetForType (final RelationshipType eType) + { + if (maTypeToTargetsMap.get(eType).size() != 1) + { + System.out.printf("there are %d targets for relationship type %s\n", + maTypeToTargetsMap.get(eType).size(), + eType.toString()); + for (final PartName aName : maTypeToTargetsMap.get(eType)) + { + System.out.printf("%s\n", aName); + } + assert(false); + } + return maTypeToTargetsMap.get(eType).firstElement(); + } + + + + + private final Map<String,PartName> maIdToTargetMap; + private final Map<RelationshipType, Vector<PartName>> maTypeToTargetsMap; +} diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/RelationshipType.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/RelationshipType.java new file mode 100644 index 000000000000..3074d1b9b52a --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/RelationshipType.java @@ -0,0 +1,112 @@ +package org.apache.openoffice.ooxml.framework.part; + +public enum RelationshipType +{ + ExtendedProperties, + CoreProperties, + OfficeDocument, + Image, + Header, + Hyperlink, + Styles, + EndNotes, + Footer, + Numbering, + CustomXML, + FootNotes, + WebSettings, + Theme, + Settings, + FontTable, + Thumbnail, + Slide, + ViewProperties, + PresentationProperties, + HandoutMaster, + TableStyles, + SlideMaster, + NotesMaster, + SlideLayout, + NotesSlide, + VMLDrawing, + OLE, + Chart, + Package, + ThemeOverride, + + Unknown + ; + + public static RelationshipType CreateFromString (final String sType) + { + switch(sType) + { + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties": + return ExtendedProperties; + case "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties": + return CoreProperties; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument": + return OfficeDocument; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image": + return Image; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header": + return Header; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink": + return Hyperlink; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles": + return Styles; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes": + return EndNotes; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer": + return Footer; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering": + return Numbering; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml": + return CustomXML; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes": + return FootNotes; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings": + return WebSettings; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme": + return Theme; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings": + return Settings; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable": + return FontTable; + case "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail": + return Thumbnail; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide": + return Slide; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps": + return ViewProperties; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps": + return PresentationProperties; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/handoutMaster": + return HandoutMaster; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles": + return TableStyles; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster": + return SlideMaster; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster": + return NotesMaster; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout": + return SlideLayout; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide": + return NotesSlide; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing": + return VMLDrawing; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject": + return OLE; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart": + return Chart; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package": + return Package; + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/themeOverride": + return ThemeOverride; + + default: + System.err.printf(sType +" is not yet supported\n"); + return Unknown; + } + } +} diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/parser/ContentTypesParser.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/parser/ContentTypesParser.java new file mode 100644 index 000000000000..0d64f10cb1c3 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/parser/ContentTypesParser.java @@ -0,0 +1,30 @@ +package org.apache.openoffice.ooxml.framework.part.parser; + +import java.io.File; +import java.io.InputStream; +import java.util.Vector; + +import org.apache.openoffice.ooxml.parser.Parser; +import org.apache.openoffice.ooxml.parser.StateMachine; + +public class ContentTypesParser + extends Parser +{ + public ContentTypesParser ( + final InputStream aIn, + final String sParserTableFilename, + final Vector<String> aErrorsAndWarnings) + { + super(CreateStateMachine(sParserTableFilename, aErrorsAndWarnings), aIn); + } + + + + + private static StateMachine CreateStateMachine ( + final String sParserTableFilename, + final Vector<String> aErrorsAndWarnings) + { + return new StateMachine(new File(sParserTableFilename), aErrorsAndWarnings); + } +} diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/parser/ParserFactory.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/parser/ParserFactory.java new file mode 100644 index 000000000000..83a635ca4077 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/parser/ParserFactory.java @@ -0,0 +1,46 @@ +package org.apache.openoffice.ooxml.framework.part.parser; + +import java.io.File; +import java.io.InputStream; +import java.util.Vector; + +import org.apache.openoffice.ooxml.framework.part.ContentType; +import org.apache.openoffice.ooxml.parser.Parser; +import org.apache.openoffice.ooxml.parser.StateMachine; + +public class ParserFactory +{ + public static Parser getParser ( + final ContentType eType, + final InputStream aStream, + final Vector<String> aErrorsAndWarnings) + { + switch(eType) + { + case Relationships: + return new RelationshipParser(aStream, msParserTableFilename, aErrorsAndWarnings); + + case ContentTypes: + return new ContentTypesParser(aStream, msParserTableFilename, aErrorsAndWarnings); + + default: + return new Parser( + new StateMachine(new File(msParserTableFilename), aErrorsAndWarnings), + aStream); + } + } + + + + + public static void SetParserTableFilename (final String sFilename) + { + assert(new File(sFilename).exists()); + msParserTableFilename = sFilename; + } + + + + + private static String msParserTableFilename = null; +} diff --git a/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/parser/RelationshipParser.java b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/parser/RelationshipParser.java new file mode 100644 index 000000000000..bcbfd3344908 --- /dev/null +++ b/ooxml/source/framework/JavaPartManager/src/org/apache/openoffice/ooxml/framework/part/parser/RelationshipParser.java @@ -0,0 +1,30 @@ +package org.apache.openoffice.ooxml.framework.part.parser; + +import java.io.File; +import java.io.InputStream; +import java.util.Vector; + +import org.apache.openoffice.ooxml.parser.Parser; +import org.apache.openoffice.ooxml.parser.StateMachine; + +public class RelationshipParser + extends Parser +{ + public RelationshipParser ( + final InputStream aIn, + final String sParserTableFilename, + final Vector<String> aErrorsAndWarnings) + { + super(CreateStateMachine(sParserTableFilename, aErrorsAndWarnings), aIn); + } + + + + + private static StateMachine CreateStateMachine ( + final String sParserTableFilename, + final Vector<String> aErrorsAndWarnings) + { + return new StateMachine(new File(sParserTableFilename), aErrorsAndWarnings); + } +} diff --git a/ooxml/source/framework/OOXMLViewer/.classpath b/ooxml/source/framework/OOXMLViewer/.classpath new file mode 100755 index 000000000000..ecd0bc81845d --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/.classpath @@ -0,0 +1,8 @@ +<?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 combineaccessrules="false" kind="src" path="/JavaPartManager"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/JavaOOXMLParser"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/ooxml/source/framework/OOXMLViewer/.project b/ooxml/source/framework/OOXMLViewer/.project new file mode 100755 index 000000000000..51a963140df1 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>OOXMLViewer</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/OOXMLViewer/.settings/org.eclipse.jdt.core.prefs b/ooxml/source/framework/OOXMLViewer/.settings/org.eclipse.jdt.core.prefs new file mode 100755 index 000000000000..838bd9d69424 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/.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/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/DetailViewManager.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/DetailViewManager.java new file mode 100755 index 000000000000..5bf1e194cfbd --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/DetailViewManager.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.viewer; + +import java.util.Vector; + +import javax.swing.JScrollPane; + +import org.apache.openoffice.ooxml.framework.part.ContentType; +import org.apache.openoffice.ooxml.framework.part.OOXMLPackage; +import org.apache.openoffice.ooxml.framework.part.Part; +import org.apache.openoffice.ooxml.framework.part.PartName; +import org.apache.openoffice.ooxml.framework.part.parser.ParserFactory; +import org.apache.openoffice.ooxml.viewer.tokenview.TokenView; +import org.apache.openoffice.ooxml.viewer.xml.TokenType; +import org.apache.openoffice.ooxml.viewer.xml.XMLTokenViewFactory; +import org.apache.openoffice.ooxml.viewer.xmltokenview.XMLViewFactory; + +public class DetailViewManager +{ + public DetailViewManager ( + final JScrollPane aDetailViewContainer, + final OOXMLPackage aPackage) + { + maDetailViewContainer = aDetailViewContainer; + maPackage = aPackage; + maViewFactory = new XMLTokenViewFactory(); + } + + + + + public void ShowPart ( + final PartName aName, + final ContentType eType) + { + ShowPart(maPackage.getPart(aName)); + } + + + + + public void ShowPart (final Part aPart) + { + switch(aPart.getContentType()) + { + case ImageGIF: + case ImageJPG: + case ImagePNG: + maDetailViewContainer.setViewportView( + new ImageView(aPart.getStream()).GetComponent()); + break; + + case ApplicationDrawing: + case ApplicationExcel: + case ApplicationXML: + case Chart: + case ContentTypes: + case CoreProperties: + case CustomXMLProperties: + case ExtendedProperties: + case PmlDocument: + case PmlHandoutMaster: + case PmlNotesMaster: + case PmlNotesSlide: + case PmlProperties: + case PmlSlide: + case PmlSlideLayout: + case PmlSlideMaster: + case PmlTableStyles: + case PmlViewProperties: + case Relationships: + case SmlSheet: + case Theme: + case ThemeOverride: + case WmlDocument: + case WmlEndNotes: + case WmlFontTable: + case WmlFootNotes: + case WmlFooter: + case WmlHeader: + case WmlNumbering: + case WmlSettings: + case WmlStyles: + case WmlWebSettings: + final TokenView<TokenType> aTokenView = maViewFactory.Create(aPart.getStream()); + maDetailViewContainer.setViewportView(aTokenView); + final Vector<String> aErrorsAndWarnings = new Vector<>(); + XMLViewFactory.AddSemanticInformation( + aTokenView, + ParserFactory.getParser( + aPart.getContentType(), + aPart.getStream(), + aErrorsAndWarnings), + aErrorsAndWarnings); + break; + + case OleObject: + case Unknown: + default: + maDetailViewContainer.setViewportView(null); + break; + } + } + + + + + private final JScrollPane maDetailViewContainer; + private XMLTokenViewFactory maViewFactory; + private OOXMLPackage maPackage; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/ImageView.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/ImageView.java new file mode 100755 index 000000000000..b7ea3baf4c7c --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/ImageView.java @@ -0,0 +1,75 @@ +/************************************************************** +* +* 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.viewer; + +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; + +import javax.imageio.ImageIO; +import javax.swing.JComponent; +import javax.swing.JPanel; + +@SuppressWarnings("serial") +public class ImageView + extends JPanel +{ + public ImageView (final InputStream aInputStream) + { + BufferedImage aImage = null; + try + { + aImage = ImageIO.read(aInputStream); + } + catch (IOException e) + { + e.printStackTrace(); + } + maImage = aImage; + } + + + + + public JComponent GetComponent() + { + return this; + } + + + + + @Override + public void paintComponent (final Graphics aGraphics) + { + super.paintComponent(aGraphics); + if (maImage != null) + { + aGraphics.drawImage(maImage, 10, 10, null); + } + } + + + + private final BufferedImage maImage; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/KeyListener.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/KeyListener.java new file mode 100755 index 000000000000..ed752ef8e465 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/KeyListener.java @@ -0,0 +1,50 @@ +/************************************************************** +* +* 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.viewer; + +import java.awt.event.KeyEvent; + +public class KeyListener + implements java.awt.event.KeyListener +{ + + @Override + public void keyTyped(KeyEvent e) + { + System.out.println(e); + } + + @Override + public void keyPressed(KeyEvent e) + { + // TODO Auto-generated method stub + + } + + @Override + public void keyReleased(KeyEvent e) + { + // TODO Auto-generated method stub + + } + +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/OOXMLViewer.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/OOXMLViewer.java new file mode 100755 index 000000000000..cc7f3636eb2f --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/OOXMLViewer.java @@ -0,0 +1,208 @@ +/************************************************************** +* +* 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.viewer; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.Vector; + +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.UIManager; + +import org.apache.openoffice.ooxml.framework.part.OOXMLPackage; +import org.apache.openoffice.ooxml.framework.part.parser.ParserFactory; +import org.apache.openoffice.ooxml.parser.Log; +import org.apache.openoffice.ooxml.viewer.content.ContentView; + +/** A simple viewer for the streams inside an OOXML file. + */ +public class OOXMLViewer +{ + public static void main (final String[] aArguments) + { + try + { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch (Exception e) + { + e.printStackTrace(); + } + + Log.Dbg = null; + + // Process options. + final Vector<String> aFilenames = new Vector<>(); + for (int nIndex=0; nIndex<aArguments.length; ++nIndex) + { + if (aArguments[nIndex].startsWith("-")) + { + switch (aArguments[nIndex]) + { + case "-t": + ++nIndex; + if (nIndex >= aArguments.length) + { + System.err.printf("expecting argument after option '-t'"); + System.exit(1); + } + ParserFactory.SetParserTableFilename(aArguments[nIndex]); + break; + + default: + System.out.printf("unknown option '%s'\n", aArguments[nIndex]); + System.exit(1);; + break; + + } + } + else + aFilenames.add(aArguments[nIndex]); + } + + for (final String sFilename : aFilenames) + { + final OOXMLViewer aViewer = new OOXMLViewer(); + aViewer.SetFile(new File(sFilename)); + aViewer.maFrame.setVisible(true); + } + } + + + + + private OOXMLViewer () + { + maFrame = new JFrame("OOXML Viewer"); + maFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + maFrame.setSize(1024,768); + maContainer = new JPanel(); + maFrame.add(maContainer, BorderLayout.CENTER); + + maFrame.setJMenuBar(CreateMenuBar()); + maFrame.addKeyListener(new KeyListener()); + } + + + + + private void SetFile (final File aFile) + { + if ( ! aFile.exists()) + { + JOptionPane.showMessageDialog( + maFrame, + "File '"+aFile.toString()+"' does not exist", + "File Error", + JOptionPane.ERROR_MESSAGE); + } + else if ( ! aFile.canRead()) + { + JOptionPane.showMessageDialog( + maFrame, + "Can not open '"+aFile.toString()+"' for reading", + "File Error", + JOptionPane.ERROR_MESSAGE); + } + else + { + maContainer.removeAll(); + maContainer.setLayout(new BorderLayout()); + + final OOXMLPackage aPackage = OOXMLPackage.Create(aFile); + final JScrollPane aDetailViewContainer = new JScrollPane( + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + aDetailViewContainer.getVerticalScrollBar().setUnitIncrement(10); + final DetailViewManager aDetailViewManager = new DetailViewManager(aDetailViewContainer, aPackage); + + final JTabbedPane aLeftSidebar = new JTabbedPane(); + + final ContentView aContentView = new ContentView(aDetailViewManager, aPackage); + aLeftSidebar.addTab("Content", new JScrollPane(aContentView)); + + final StreamView aFragmentView = new StreamView(aDetailViewManager, aPackage); + aLeftSidebar.addTab("Streams", new JScrollPane(aFragmentView)); + + final PartsView aPartsView = new PartsView(aDetailViewManager, aPackage); + aLeftSidebar.addTab("Parts", new JScrollPane(aPartsView)); + + final JSplitPane aPane = new JSplitPane( + JSplitPane.HORIZONTAL_SPLIT, + aLeftSidebar, + aDetailViewContainer + ); + aPane.setDividerLocation(200); + + maContainer.add(aPane, BorderLayout.CENTER); + + aFragmentView.ShowInitialPart(); + } + } + + + + + private JMenuBar CreateMenuBar () + { + final JMenuBar aMenuBar = new JMenuBar(); + + final JMenu aFileMenu = new JMenu("File"); + aMenuBar.add(aFileMenu); + + final JMenuItem aOpenItem = new JMenuItem("Open"); + aOpenItem.addActionListener(new ActionListener() + { + @Override public void actionPerformed (final ActionEvent aEvent) + { + final JFileChooser aFileChooser = new JFileChooser(); + final int nResult = aFileChooser.showOpenDialog(null); + if (nResult == JFileChooser.APPROVE_OPTION) + { + final OOXMLViewer aViewer = new OOXMLViewer(); + aViewer.SetFile(aFileChooser.getSelectedFile()); + } + } + }); + aFileMenu.add(aOpenItem); + + return aMenuBar; + } + + + + + private final JFrame maFrame; + private final JComponent maContainer; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/PartsView.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/PartsView.java new file mode 100755 index 000000000000..caa9b3a9e308 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/PartsView.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.viewer; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; +import java.util.TreeSet; + +import javax.swing.JTree; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +import org.apache.openoffice.ooxml.framework.part.ContentType; +import org.apache.openoffice.ooxml.framework.part.IReferenceProvider; +import org.apache.openoffice.ooxml.framework.part.OOXMLPackage; +import org.apache.openoffice.ooxml.framework.part.Part; +import org.apache.openoffice.ooxml.framework.part.PartName; + +@SuppressWarnings("serial") +public class PartsView + extends JTree + implements TreeSelectionListener +{ + public PartsView ( + final DetailViewManager aDetailViewManager, + final OOXMLPackage aPackage) + { + maPackage = aPackage; + maDetailViewManager = aDetailViewManager; + + Initialize(); + + getSelectionModel().addTreeSelectionListener(this); + } + + + + private final void Initialize () + { + final DefaultMutableTreeNode aRootNode = new DefaultMutableTreeNode( + "top level relations"); + final TreeModel aModel = new DefaultTreeModel(aRootNode); + + final Queue<IReferenceProvider> aWorklist = new LinkedList<IReferenceProvider>(); + aWorklist.add(maPackage); + final Set<PartName> aProcessedPartNames = new TreeSet<PartName>(); + CreateChildren(aRootNode, aProcessedPartNames, maPackage); + setModel(aModel); + } + + + + + private void CreateChildren ( + final DefaultMutableTreeNode aNode, + final Set<PartName> aProcessedPartNames, + final IReferenceProvider aReferences) + { + for (final PartName aTarget : aReferences.getRelatedParts().getAllTargets()) + { + Part aPart = maPackage.getPart(aTarget); + final DefaultMutableTreeNode aRelationNode; + if ( ! aProcessedPartNames.contains(aPart.getPartName())) + { + aProcessedPartNames.add(aPart.getPartName()); + aRelationNode = new DefaultMutableTreeNode(aPart.getPartName().GetFullname()); + CreateChildren(aRelationNode, aProcessedPartNames, aPart); + } + else + { + aRelationNode = new DefaultMutableTreeNode(aPart.getPartName().GetFullname() + "..."); + } + aNode.add(aRelationNode); + } + } + + + + + /** Callback for clicks on the part view. + */ + @Override + public void valueChanged (final TreeSelectionEvent aEvent) + { + final TreePath aPath = aEvent.getNewLeadSelectionPath(); + if (aPath != null) + { + final PartName aName= GetPackagePathForTreePath(aPath); + final ContentType eType = maPackage.getPart( + aName).getContentType(); + maDetailViewManager.ShowPart( + aName, + eType); + } + } + + + + + + private PartName GetPackagePathForTreePath (final TreePath aPath) + { + final PartName aName = new PartName(aPath.getLastPathComponent().toString()); + return aName; + } + + + + + + private final OOXMLPackage maPackage; + private final DetailViewManager maDetailViewManager; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/StreamView.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/StreamView.java new file mode 100755 index 000000000000..61d497d02a5f --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/StreamView.java @@ -0,0 +1,266 @@ +/************************************************************** +* +* 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.viewer; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; + +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import org.apache.openoffice.ooxml.framework.part.ContentType; +import org.apache.openoffice.ooxml.framework.part.OOXMLPackage; +import org.apache.openoffice.ooxml.framework.part.Part; +import org.apache.openoffice.ooxml.framework.part.PartName; + +/** Overview of the individual parts/streams in an OOXML file. + */ +@SuppressWarnings("serial") +public class StreamView + extends JTree + implements TreeSelectionListener, MouseMotionListener +{ + /** Create a new PartsView object for the given OOXML file. + * When the user clicks on one part entry then the DetailViewManager is + * called and asked to display the part. + */ + StreamView ( + final DetailViewManager aDetailViewManager, + final OOXMLPackage aPackage) + { + maDetailViewManager = aDetailViewManager; + maOOXMLPackage = aPackage; + getSelectionModel().addTreeSelectionListener(this); + + // Listen for motion events so that the tooltip can be set according to + // the entry under the mouse pointer. + addMouseMotionListener(this); + + // Create a tree model for the streams in the (zip) file, set the model + // asynchronously at the JTree and finally expand all nodes. + final TreeModel aModel = CreateTreeModel(aPackage); + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + setModel(aModel); + for (int nIndex=0; nIndex<getRowCount(); ++nIndex) + { + expandRow(nIndex); + } + } + }); + + setToolTipText("hallo"); + } + + + + + private TreeModel CreateTreeModel (final OOXMLPackage aPackage) + { + final DefaultMutableTreeNode aRootNode = new DefaultMutableTreeNode( + aPackage.getFileName()); + final DefaultTreeModel aModel = new DefaultTreeModel(aRootNode); + try + { + for (final String sStreamName : aPackage.listStreamNames()) + { + DefaultMutableTreeNode aNode = aRootNode; + for (final String sPart : sStreamName.split("/")) + { + DefaultMutableTreeNode aChild = GetChildNodeForName(aNode, sPart); + if (aChild == null) + { + aChild = new DefaultMutableTreeNode(sPart); + aNode.add(aChild); + } + + aNode = aChild; + } + } + } + catch (Exception e) + { + e.printStackTrace(); + } + return aModel; + } + + + + + public void ShowInitialPart () + { + final Part aPart = maOOXMLPackage.getOfficeDocumentPart(); + MakePartVisible(aPart); + maDetailViewManager.ShowPart(aPart); + } + + + + + private DefaultMutableTreeNode GetChildNodeForName (final TreeNode aNode, final String sName) + { + for (int nIndex=0; nIndex<aNode.getChildCount(); ++nIndex) + { + final TreeNode aChild = aNode.getChildAt(nIndex); + if (aChild.toString().equals(sName)) + { + return (DefaultMutableTreeNode)aChild; + } + } + return null; + } + + + + + /** Callback for clicks on the part view. + */ + @Override + public void valueChanged (final TreeSelectionEvent aEvent) + { + final TreePath aPath = aEvent.getNewLeadSelectionPath(); + if (aPath != null) + { + final String sPath = GetPackagePathForTreePath(aPath); + final PartName aName = new PartName(sPath); + final ContentType eType = maOOXMLPackage.getPart( + aName).getContentType(); + maDetailViewManager.ShowPart( + aName, + eType); + } + } + + + + + @Override + public void mouseDragged (final MouseEvent aEvent) + { + } + + + + + @Override + public void mouseMoved (final MouseEvent aEvent) + { + final int nRow = getRowForLocation(aEvent.getX(), aEvent.getY()); + if (nRow >= 0) + { + final String sPath = GetPackagePathForTreePath(getPathForRow(nRow)); + SetPartUnderMouse(sPath); + } + } + + + + + private String GetPackagePathForTreePath (final TreePath aPath) + { + final StringBuffer sPath = new StringBuffer(""); + final Object aNodes[] = aPath.getPath(); + for (int nIndex=1; nIndex<aNodes.length; ++nIndex) + { + sPath.append("/"); + sPath.append(aNodes[nIndex].toString()); + } + return sPath.toString(); + } + + + + + private void SetPartUnderMouse (final String sPartPath) + { + if (msPartPathUnderMouse==null + || ! msPartPathUnderMouse.equals(sPartPath)) + { + msPartPathUnderMouse = sPartPath; + + final ContentType eType = maOOXMLPackage.getPart( + new PartName(msPartPathUnderMouse)).getContentType(); + String sToolTipText = eType.toString(); + if ( ! eType.GetLongName().isEmpty()) + sToolTipText += " (" + eType.GetLongName() + ")"; + setToolTipText(sToolTipText); + } + } + + + + + private void MakePartVisible (final Part aPart) + { + final String[] aPathParts = aPart.getPartName().GetFullname().substring(1).split("/"); + final TreeNode[] aTreeNodePath = new TreeNode[aPathParts.length+1]; + TreeNode aNode = (TreeNode)getModel().getRoot(); + int nDepth = 0; + aTreeNodePath[nDepth++] = aNode; + for (final String sPathPart : aPathParts) + { + boolean bFoundChild = false; + for (int nIndex=0; nIndex<aNode.getChildCount(); ++nIndex) + { + final TreeNode aChildNode = aNode.getChildAt(nIndex); + final String sChildName = aChildNode.toString(); + if (sChildName.equals(sPathPart)) + { + aNode = aChildNode; + aTreeNodePath[nDepth++] = aNode; + bFoundChild = true; + break; + } + } + if ( ! bFoundChild) + return; + } + + SelectNode(new TreePath(aTreeNodePath)); + } + + + + + private void SelectNode (final TreePath aTreePath) + { +// getSelectionModel().setSelectionPath(aTreePath); +// scrollPathToVisible(aTreePath); + } + + + + + private final DetailViewManager maDetailViewManager; + private final OOXMLPackage maOOXMLPackage; + private String msPartPathUnderMouse; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/ContentView.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/ContentView.java new file mode 100755 index 000000000000..a5049813ba5d --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/ContentView.java @@ -0,0 +1,77 @@ +/************************************************************** +* +* 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.viewer.content; + +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeModel; + +import org.apache.openoffice.ooxml.framework.part.OOXMLPackage; +import org.apache.openoffice.ooxml.viewer.DetailViewManager; + +/** Overview of the individual parts/streams in an OOXML file. + */ +@SuppressWarnings("serial") +public class ContentView + extends JTree +{ + public ContentView ( + final DetailViewManager aDetailViewManager, + final OOXMLPackage aPackage) + { + TreeModel aModel = null; + switch(aPackage.getOfficeDocumentPart().getContentType()) + { + case PmlDocument: + aModel = CreateTreeForPresentationModel(new PresentationImporter().importModel( + aPackage.getOfficeDocumentPart())); + break; + //case SmlDocument: + //case WmlDocument: + default: + break; + } + if (aModel != null) + setModel(aModel); + } + + + + + + + private TreeModel CreateTreeForPresentationModel (final PresentationModel aModel) + { + final DefaultMutableTreeNode aRootNode = new DefaultMutableTreeNode( + "presentation"); + final DefaultTreeModel aTreeModel = new DefaultTreeModel(aRootNode); + + for (final Slide aSlide : aModel.GetSlideManager().GetSlides()) + { + DefaultMutableTreeNode aNode = new DefaultMutableTreeNode( + aSlide.toString()); + aRootNode.add(aNode); + } + return aTreeModel; + } +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/PresentationImporter.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/PresentationImporter.java new file mode 100755 index 000000000000..5581ca41887a --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/PresentationImporter.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.viewer.content; + +import java.io.InputStream; +import java.util.Vector; + +import javax.xml.stream.Location; + +import org.apache.openoffice.ooxml.framework.part.ContentType; +import org.apache.openoffice.ooxml.framework.part.Part; +import org.apache.openoffice.ooxml.framework.part.parser.ParserFactory; +import org.apache.openoffice.ooxml.parser.ElementContext; +import org.apache.openoffice.ooxml.parser.Parser; +import org.apache.openoffice.ooxml.parser.action.ActionTrigger; +import org.apache.openoffice.ooxml.parser.action.IAction; + +public class PresentationImporter +{ + public PresentationModel importModel (final Part aPart) + { + final PresentationModel aModel = new PresentationModel(); + final Parser aParser = CreateParser( + aPart.getStream(), + aModel, + aPart); + aParser.Parse(); + return aModel; + } + + + + private Parser CreateParser ( + final InputStream aStream, + final PresentationModel aModel, + final Part aPart) + { + final Parser aParser = ParserFactory.getParser( + ContentType.PmlDocument, + aStream, + new Vector<String>()); + + aParser.GetActionManager().AddElementStartAction( + "p06_CT_SlideIdListEntry", + new IAction() + { + @Override public void Run(ActionTrigger eTrigger, + ElementContext aContext, String sText, + Location aStartLocation, Location aEndLocation) + { + final String aReferenceId = aContext.GetAttributes().GetRawAttributeValue("r06_id"); + + aModel.GetSlideManager().GetSlideForPart( + aPart.getPartById(aReferenceId)); + + System.out.println(aReferenceId); + } + }); + + return aParser; + } +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/PresentationModel.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/PresentationModel.java new file mode 100755 index 000000000000..b111ad58b382 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/PresentationModel.java @@ -0,0 +1,43 @@ +/************************************************************** +* +* 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.viewer.content; + +public class PresentationModel +{ + PresentationModel() + { + maSlideManager = new SlideManager(); + } + + + + + public SlideManager GetSlideManager() + { + return maSlideManager; + } + + + + + private final SlideManager maSlideManager; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/Slide.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/Slide.java new file mode 100755 index 000000000000..3dc09db12cfd --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/Slide.java @@ -0,0 +1,56 @@ +/************************************************************** +* +* 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.viewer.content; + +public class Slide +{ + Slide ( + final String sSlideName, + final int nShapeCount) + { + msSlideName = sSlideName; + mnShapeCount = nShapeCount; + } + + + + + public String getSlideName () + { + return msSlideName; + } + + + + + @Override + public String toString () + { + return msSlideName +" has "+mnShapeCount+" shapes"; + } + + + + + private final String msSlideName; + private final int mnShapeCount; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/SlideManager.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/SlideManager.java new file mode 100755 index 000000000000..153ac3986ec6 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/SlideManager.java @@ -0,0 +1,77 @@ +/************************************************************** +* +* 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.viewer.content; + +import java.util.Map; +import java.util.TreeMap; +import java.util.Vector; + +import org.apache.openoffice.ooxml.framework.part.Part; +import org.apache.openoffice.ooxml.framework.part.PartName; + +public class SlideManager +{ + SlideManager () + { + maSlides = new Vector<>(); + maPartNameToSlideMap = new TreeMap<>(); + } + + + + public void AddSlide ( + final PartName aName, + final Slide aSlide) + { + maSlides.add(aSlide); + maPartNameToSlideMap.put(aName, aSlide); + } + + + + + public Iterable<Slide> GetSlides () + { + return maSlides; + } + + + + + public Slide GetSlideForPart (final Part aPart) + { + Slide aSlide = maPartNameToSlideMap.get(aPart.getPartName()); + if (aSlide == null) + { + aSlide = new SlideParser(aPart).ParseSlide(maSlides.size()); + + AddSlide(aPart.getPartName(), aSlide); + } + return aSlide; + } + + + + + private final Vector<Slide> maSlides; + private final Map<PartName,Slide> maPartNameToSlideMap; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/SlideParser.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/SlideParser.java new file mode 100755 index 000000000000..82dfedbdfcd2 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/content/SlideParser.java @@ -0,0 +1,80 @@ +/************************************************************** +* +* 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.viewer.content; + +import java.util.Vector; + +import javax.xml.stream.Location; + +import org.apache.openoffice.ooxml.framework.part.Part; +import org.apache.openoffice.ooxml.framework.part.parser.ParserFactory; +import org.apache.openoffice.ooxml.parser.ElementContext; +import org.apache.openoffice.ooxml.parser.Parser; +import org.apache.openoffice.ooxml.parser.action.ActionTrigger; +import org.apache.openoffice.ooxml.parser.action.IAction; + +public class SlideParser +{ + SlideParser (final Part aPart) + { + maParser = ParserFactory.getParser( + aPart.getContentType(), + aPart.getStream(), + new Vector<String>()); + mnShapeCount = 0; + + maParser.GetActionManager().AddElementStartAction( + "p06_CT_Shape", + new IAction() + { + @Override public void Run(ActionTrigger eTrigger, ElementContext aContext, + String sText, Location aStartLocation, Location aEndLocation) + { + SlideParser.this.IncreaseShapeCount(); + } + }); + } + + + + + Slide ParseSlide (final int nSlideIndex) + { + maParser.Parse(); + final Slide aSlide = new Slide("slide "+nSlideIndex, mnShapeCount); + return aSlide; + } + + + + + private void IncreaseShapeCount () + { + ++mnShapeCount; + } + + + + + private final Parser maParser; + private int mnShapeCount; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/DocumentFactory.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/DocumentFactory.java new file mode 100755 index 000000000000..11f2ac566945 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/DocumentFactory.java @@ -0,0 +1,127 @@ +/************************************************************** +* +* 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.viewer.tokenview; + +import java.util.Stack; + + +public class DocumentFactory<TokenType> +{ + interface IRepaintTarget + { + void RequestRepaint(); + } + + + + + DocumentFactory ( + final LineContainer<TokenType> aLines, + final IRepaintTarget aRepaintTarget) + { + maLines = aLines; + maRepaintTarget = aRepaintTarget; + mbIsGroupBeginPending = false; + maGroupStartStack = new Stack<Run<TokenType>>(); + maLastRun = null; + maCurrentGroup = null; + StartNewLine(); + } + + + + + public void AddText ( + final String sText, + final TokenType eTokenType, + final Style aStyle, + final int nOffset) + { + final Run<TokenType> aRun = new Run<TokenType>(sText, eTokenType, aStyle, nOffset); + synchronized(maLines) + { + maCurrentLine.AddRun(aRun); + if (sText.endsWith("\n")) + StartNewLine(); + } + + if (mbIsGroupBeginPending) + { + maGroupStartStack.push(maCurrentGroup); + aRun.SetGroupParent(maCurrentGroup); + maCurrentGroup = aRun; + mbIsGroupBeginPending = false; + } + else if (maCurrentGroup != null) + aRun.SetGroupParent(maCurrentGroup); + maLastRun = aRun; + + maRepaintTarget.RequestRepaint(); + } + + + + + public void FinishText () + { + StartNewLine(); + } + + + + + public void BeginGroup() + { + mbIsGroupBeginPending = true; + } + + + + + public void EndGroup () + { + maCurrentGroup.SetGroupEnd(maLastRun); + maCurrentGroup = maGroupStartStack.pop(); + } + + + + + + private void StartNewLine () + { + if (maCurrentLine != null) + maLines.AddLine(maCurrentLine); + maCurrentLine = new Line<TokenType>(); + } + + + + + private final LineContainer<TokenType> maLines; + private final IRepaintTarget maRepaintTarget; + private Line<TokenType> maCurrentLine; + private boolean mbIsGroupBeginPending; + private Stack<Run<TokenType>> maGroupStartStack; + private Run<TokenType> maLastRun; + private Run<TokenType> maCurrentGroup; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/FormatState.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/FormatState.java new file mode 100755 index 000000000000..071ac1426472 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/FormatState.java @@ -0,0 +1,61 @@ +/************************************************************** +* +* 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.viewer.tokenview; + +import java.awt.Dimension; +import java.util.Iterator; +import java.util.Vector; + +public class FormatState<TokenType> + implements Iterable<Line<TokenType>> +{ + FormatState ( + final Dimension aBoundingSize, + final Vector<Line<TokenType>> aVisibleLines) + { + maBoundingSize = aBoundingSize; + maVisibleLines = aVisibleLines; + } + + + + + public Dimension GetTextBoundingSize() + { + return maBoundingSize; + } + + + + + @Override + public Iterator<Line<TokenType>> iterator () + { + return maVisibleLines.iterator(); + } + + + + + private final Dimension maBoundingSize; + private final Vector<Line<TokenType>> maVisibleLines; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Formatter.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Formatter.java new file mode 100755 index 000000000000..605aeab38950 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Formatter.java @@ -0,0 +1,93 @@ +/************************************************************** +* +* 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.viewer.tokenview; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.util.Vector; + +public class Formatter<TokenType> +{ + public Formatter () + { + mnLastFormattedLine = -1; + mnWidth = 0; + mnHeight = 0; + } + + + + + public FormatState<TokenType> FormatText ( + final Graphics2D aG2, + final LineContainer<TokenType> aLines) + { + FormatLines(aG2, aLines); + + final double nTop = aG2.getClipBounds().getMinY(); + final double nBottom = aG2.getClipBounds().getMaxY(); + final Vector<Line<TokenType>> aVisibleLines = new Vector<>(); + for (final Line<TokenType> aLine : aLines.GetLines()) + { + if (aLine.Overlaps(nTop, nBottom)) + { + // Line is (partially) visible. + aVisibleLines.add(aLine); + } + } + + return new FormatState<TokenType>( + new Dimension(mnWidth,mnHeight), + aVisibleLines); + } + + + + + private void FormatLines ( + final Graphics2D aG2, + final LineContainer<TokenType> aLines) + { + for (int nIndex=mnLastFormattedLine+1,nCount=aLines.GetLineCount(); nIndex<nCount; ++nIndex) + { + final Line<TokenType> aLine = aLines.GetLine(nIndex); + final int nY; + if (nIndex > 0) + nY = aLines.GetLine(nIndex-1).GetBottom(); + else + nY = 0; + aLine.Format(aG2, nY); + if (aLine.GetWidth() > mnWidth) + mnWidth = aLine.GetWidth(); + if (aLine.GetBottom() > mnHeight) + mnHeight = aLine.GetBottom(); + } + mnLastFormattedLine = aLines.GetLineCount()-1; + } + + + + + private int mnLastFormattedLine; + private int mnWidth; + private int mnHeight; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Line.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Line.java new file mode 100755 index 000000000000..c39e24bf6ed3 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Line.java @@ -0,0 +1,239 @@ +/************************************************************** +* +* 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.viewer.tokenview; + +import java.awt.Graphics2D; +import java.util.Iterator; +import java.util.Vector; + +public class Line<TokenType> + + implements Iterable<Run<TokenType>> +{ + Line () + { + maRuns = new Vector<Run<TokenType>>(); + mnWidth = -1; + mnHeight = -1; + mnY = -1; + mnStartOffset = -1; + mnEndOffset = -1; + } + + + + + public void AddRun (final Run<TokenType> aRun) + { + maRuns.add(aRun); + mnWidth += aRun.GetWidth(); + if (aRun.GetHeight() > mnHeight) + mnHeight = aRun.GetHeight(); + aRun.SetLine(this); + + if (aRun.GetStreamOffset() >= 0) + { + if (mnStartOffset < 0) + mnStartOffset = aRun.GetStreamOffset(); + + if (mnEndOffset < aRun.GetStreamEndOffset()) + mnEndOffset = aRun.GetStreamEndOffset(); + } + } + + + + + public void Format ( + final Graphics2D aG2, + final int nY) + { + mnY = nY; + + mnWidth = 0; + mnHeight = 0; + for (final Run<TokenType> aRun : maRuns) + { + aRun.Format(aG2); + mnWidth += aRun.GetWidth(); + if (mnHeight < aRun.GetHeight()) + mnHeight = aRun.GetHeight(); + } + } + + + + + public int GetWidth () + { + return mnWidth; + } + + + + + public int GetHeight () + { + return mnHeight; + } + + + + + public int GetTop () + { + return mnY; + } + + + + + public int GetBottom () + { + return mnY + mnHeight; + } + + + + + public boolean Overlaps ( + final double nTop, + final double nBottom) + { + return mnY<=nBottom && mnY+mnHeight>nTop; + } + + + + + public boolean Contains (final int nY) + { + return nY>=mnY && nY<mnY+mnHeight; + } + + + + + @Override + public Iterator<Run<TokenType>> iterator() + { + return maRuns.iterator(); + } + + + + + public Run<TokenType> GetRunForX (final int nX) + { + int nRunX = 0; + for (final Run<TokenType> aRun : maRuns) + { + final int nRunWidth = aRun.GetWidth(); + final int nRight = nRunX + nRunWidth; + if (nX>=nRunX && nX<nRight) + return aRun; + nRunX = nRight; + } + return null; + } + + + + + public Run<TokenType> GetRunForOffset (int nOffset) + { + for (int nIndex=0; nIndex<maRuns.size(); ++nIndex) + { + final Run<TokenType> aRun = maRuns.get(nIndex); + final int nRunOffset = aRun.GetStreamOffset(); + if (nRunOffset >= 0) + if (nRunOffset<=nOffset && nOffset<=aRun.GetStreamEndOffset()) + return aRun; + } + return null; + } + + + + + public Iterable<Run<TokenType>> GetRunsForOffsets ( + final int nStartOffset, + final int nEndOffset) + { + final Vector<Run<TokenType>> aRuns = new Vector<>(); + + for (final Run<TokenType> aRun : maRuns) + { + if (aRun.GetStreamOffset() >= nEndOffset) + break; + else if (aRun.GetStreamEndOffset()<nStartOffset) + continue; + else + aRuns.add(aRun); + } + + return aRuns; + } + + + + + public int GetStartOffset() + { + return mnStartOffset; + } + + + + + public int GetEndOffset () + { + return mnEndOffset; + } + + + + + public boolean ContainsOffset (final int nOffset) + { + return mnStartOffset<=nOffset && nOffset<mnEndOffset; + } + + + + + @Override + public String toString () + { + return String.format("line of %d runs: %s", maRuns.size(), maRuns.toString()); + } + + + + + private final Vector<Run<TokenType>> maRuns; + private int mnY; + private int mnWidth; + private int mnHeight; + private int mnStartOffset; + private int mnEndOffset; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/LineContainer.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/LineContainer.java new file mode 100755 index 000000000000..c791fb201ec8 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/LineContainer.java @@ -0,0 +1,194 @@ +/************************************************************** +* +* 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.viewer.tokenview; + +import java.util.Vector; + +public class LineContainer<TokenType> +{ + LineContainer () + { + maLines = new Vector<>(); + } + + + + + boolean IsLineInGroup ( + final Line<TokenType> aLine, + final Run<TokenType> aGroup) + { + if (aGroup == null) + return false; + + final Line<TokenType> aStartLine = aGroup.GetLine(); + if (aLine.GetTop() < aStartLine.GetTop()) + return false; + final Line<TokenType> aEndLine = aGroup.GetGroupEnd().GetLine(); + if (aLine.GetTop() > aEndLine.GetTop()) + return false; + + return true; + } + + + + + Line<TokenType> GetLineForY (final int nY) + { + int nMin = 0; + int nMax = maLines.size()-1; + if (nMin <= nMax) + { + while (nMin < nMax-1) + { + final int nMed = (nMax+nMin)/2; + if (nY < maLines.get(nMed).GetTop()) + nMax = nMed; + else + nMin = nMed; + } + for (int nIndex=nMin; nIndex<=nMax; ++nIndex) + if (maLines.get(nIndex).Contains(nY)) + return maLines.get(nIndex); + } + return null; + } + + + + + public Line<TokenType> GetLineForOffset (final int nOffset) + { + final int nLineIndex = GetLineIndexForOffset(nOffset, 0); + if (nLineIndex < 0) + return null; + else + return maLines.get(nLineIndex); + + } + + + + + public Iterable<Line<TokenType>> GetLinesForOffsets ( + final int nStartOffset, + final int nEndOffset) + { + final Vector<Line<TokenType>> aLines = new Vector<>(); + + final int nStartLineIndex = GetLineIndexForOffset(nStartOffset, -1); + final int nEndLineIndex = GetLineIndexForOffset(nEndOffset, +1); + if (nStartLineIndex >= 0) + { + if (nEndLineIndex < 0) + aLines.add(maLines.get(nStartLineIndex)); + else + for (int nIndex=nStartLineIndex; nIndex<=nEndLineIndex; ++nIndex) + aLines.add(maLines.get(nIndex)); + } + return aLines; + } + + + + + public int GetLineCount() + { + return maLines.size(); + } + + + + + public Line<TokenType> GetLine (final int nIndex) + { + return maLines.get(nIndex); + } + + + + + public Iterable<Line<TokenType>> GetLines () + { + return maLines; + } + + + + + public void AddLine (final Line<TokenType> aLine) + { + maLines.add(aLine); + } + + + + + /** Return the index of the line that contains the given offset. + * When there is no line that contains the line that look at the bias + * to return the previous or next line. + */ + private int GetLineIndexForOffset ( + final int nOffset, + final int nBias) + { + int nMinIndex = 0; + int nMaxIndex = maLines.size()-1; + while (nMinIndex < nMaxIndex-1) + { + final int nMedIndex = (nMinIndex + nMaxIndex) / 2; + if (maLines.get(nMedIndex).GetEndOffset() <= nOffset) + nMinIndex = nMedIndex; + else + nMaxIndex = nMedIndex; + } + for (int nIndex=nMinIndex; nIndex<=nMaxIndex; ++nIndex) + { + if (maLines.get(nIndex).ContainsOffset(nOffset)) + return nIndex; + } + if (nBias < 0) + { + for (int nIndex=nMinIndex; nIndex<=nMaxIndex; ++nIndex) + { + if (maLines.get(nIndex).GetStartOffset() > nOffset) + return nIndex; + } + } + else if (nBias > 0) + { + for (int nIndex=nMaxIndex; nIndex>=nMinIndex; ++nIndex) + { + if (maLines.get(nIndex).GetEndOffset() < nOffset) + return nIndex; + } + } + + return -1; + } + + + + + private final Vector<Line<TokenType>> maLines; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Run.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Run.java new file mode 100755 index 000000000000..ad2677efcb77 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Run.java @@ -0,0 +1,249 @@ +/************************************************************** +* +* 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.viewer.tokenview; + +import java.awt.Color; +import java.awt.FontMetrics; +import java.awt.Graphics2D; + +public class Run<TokenType> +{ + Run ( + final String sText, + final TokenType eTokenType, + final Style aStyle, + final int nOffset) + { + msText = sText; + meTokenType = eTokenType; + maStyle = aStyle!=null ? aStyle : Style.DefaultStyle; + mnStreamOffset = nOffset; + + mnWidth = -1; + mnHeight = -1; + mnOffset = nOffset; + maParent = null; + maGroupEnd = null; + maLine = null; + } + + + + + public void Format ( + final Graphics2D aG2) + { + final FontMetrics aMetrics = aG2.getFontMetrics(maStyle.GetFont()); + mnWidth = aMetrics.stringWidth(msText); + mnHeight = aMetrics.getHeight(); + mnOffset = -aMetrics.getDescent(); + } + + + + + public void Paint ( + final Graphics2D aG2, + final int nX, + final int nY, + final Color aBackgroundColor) + { + maStyle.Set(aG2); + + if (mnWidth < 0) + { + mnWidth = aG2.getFontMetrics().stringWidth(msText); + mnHeight = aG2.getFontMetrics().getHeight(); + } + + if (aBackgroundColor != null) + { + final Color aSavedColor = aG2.getColor(); + aG2.setColor(aBackgroundColor); + aG2.fillRect(nX,nY-mnHeight, mnWidth, mnHeight); + aG2.setColor(aSavedColor); + } + aG2.drawString(msText, nX, nY+mnOffset); + + if (msToolTipText != null) + { + aG2.drawLine(nX, nY-1, nX+mnWidth, nY-1); + } + } + + + + + public String GetText() + { + return msText; + } + + + + + public Style GetStyle () + { + return maStyle; + } + + + + + public int GetStreamOffset () + { + return mnStreamOffset; + } + + + + + public int GetStreamEndOffset () + { + return mnStreamOffset + msText.length(); + } + + + + + public int GetWidth() + { + return mnWidth; + } + + + + + public int GetHeight () + { + return mnHeight; + } + + + + + public void SetGroupParent (final Run<TokenType> aParent) + { + maParent = aParent; + } + + + + + public void SetGroupEnd (final Run<TokenType> aRun) + { + maGroupEnd = aRun; + } + + + + + public Run<TokenType> GetGroupEnd() + { + return maGroupEnd; + } + + + + + public boolean IsGroup () + { + if (maGroupEnd == null) + return false; + else if (maLine == maGroupEnd.maLine) + return true; + else + return true; + } + + + + + public Run<TokenType> GetParent () + { + return maParent; + } + + + + + public Line<TokenType> GetLine () + { + return maLine; + } + + + + + public void SetLine (final Line<TokenType> aLine) + { + maLine = aLine; + } + + + + + public void SetToolTipText (final String sText) + { + msToolTipText = sText; + } + + + + + public String GetToolTipText () + { + return msToolTipText; + } + + + + + public TokenType GetTokenType () + { + return meTokenType; + } + + + + + @Override + public String toString () + { + return "run '"+msText+"' @ "+mnOffset; + } + + + + + private final String msText; + private final TokenType meTokenType; + private final Style maStyle; + private final int mnStreamOffset; + private int mnWidth; + private int mnHeight; + private int mnOffset; + private Run<TokenType> maParent; + private Run<TokenType> maGroupEnd; + private Line<TokenType> maLine; + private String msToolTipText; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/RunRange.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/RunRange.java new file mode 100755 index 000000000000..2c980a92dd76 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/RunRange.java @@ -0,0 +1,87 @@ +/************************************************************** +* +* 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.viewer.tokenview; + +import java.util.Vector; + +public class RunRange<TokenType> +{ + RunRange (final Vector<Run<TokenType>> aRuns) + { + maRuns = aRuns; + } + + + + + public int FindTokens ( + @SuppressWarnings("unchecked") + final TokenType ... eTypeList) + { + return FindTokens(0, eTypeList); + } + + + + + public int FindTokens ( + final int nFirstIndex, + @SuppressWarnings("unchecked") + final TokenType ... eTypeList) + { + for (int nIndex=nFirstIndex; nIndex<maRuns.size()-eTypeList.length; ++nIndex) + { + boolean bMatches = true; + for (int nInnerIndex=0; nInnerIndex<eTypeList.length && bMatches; ++nInnerIndex) + if (maRuns.get(nIndex+nInnerIndex).GetTokenType() + != eTypeList[nInnerIndex]) + { + bMatches = false; + } + if (bMatches) + return nIndex; + } + + return -1; + } + + + + + public boolean IsEmpty () + { + return maRuns.isEmpty(); + } + + + + + public Run<TokenType> Get (final int nIndex) + { + return maRuns.get(nIndex); + } + + + + + private final Vector<Run<TokenType>> maRuns; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Style.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Style.java new file mode 100755 index 000000000000..3f81cd8fb76b --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/Style.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.viewer.tokenview; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; + +import javax.swing.UIManager; + +public class Style +{ + public Style () + { + maForegroundColor = Color.BLACK; + if (DefaultFont == null) + DefaultFont = UIManager.getDefaults().getFont("TextField.font"); + maFont = DefaultFont; + } + + + + + public void Set(Graphics2D aG2) + { + aG2.setColor(maForegroundColor); + aG2.setFont(maFont); + } + + + + + public Style SetForegroundColor (final Color aColor) + { + maForegroundColor = aColor; + return this; + } + + + + + public Color GetForegroundColor() + { + return maForegroundColor; + } + + + + + public Style SetBackgroundColor (final Color aColor) + { + maBackgroundColor = aColor; + return this; + } + + + + + public Color GetBackgroundColor() + { + return maBackgroundColor; + } + + + + + public Style SetBold() + { + maFont = maFont.deriveFont(Font.BOLD); + return this; + } + + + + + public Font GetFont() + { + return maFont; + } + + + + + public static final Style DefaultStyle = new Style(); + public static Font DefaultFont = null; + + private Color maBackgroundColor; + private Color maForegroundColor; + private Font maFont; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/TokenView.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/TokenView.java new file mode 100755 index 000000000000..961524f382b0 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/tokenview/TokenView.java @@ -0,0 +1,406 @@ +/************************************************************** +* +* 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.viewer.tokenview; + +import java.awt.Color; +import java.awt.Container; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.RenderingHints; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.util.Iterator; +import java.util.Vector; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.UIManager; + +/** A simple view of tokenized content. + * + * Create the content by calling GetDocumentFactory() and using the returned + * factory to add tokenized text. + */ +@SuppressWarnings("serial") +public class TokenView<TokenType> + extends JPanel + implements MouseMotionListener, DocumentFactory.IRepaintTarget, ComponentListener +{ + public TokenView () + { + maLines = new LineContainer<TokenType>(); + maFormatter = new Formatter<TokenType>(); + + addMouseMotionListener(this); + addComponentListener(this); + } + + + + + @Override + public void paintComponent (final Graphics aGraphics) + { + super.paintComponent(aGraphics); + + final Graphics2D aG2 = (Graphics2D)aGraphics; + aG2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); + + final FormatState<TokenType> aState; + synchronized(maLines) + { + aState = maFormatter.FormatText(aG2, maLines); + } + setPreferredSize(aState.GetTextBoundingSize()); + + for (final Line<TokenType> aLine : aState) + { + PaintLineHighlight(aG2, aLine); + PaintLineNumber(aG2, aLine); + + int nX = mnTextStart; + for (final Run<TokenType> aRun : aLine) + { + final Color aRunColor; + if (aRun == maRunUnderMouse) + aRunColor = maRunUnderMouseColor; + else if (aRun == maHighlightedErrorRun) + aRunColor = maErrorHighlightColor; + else + aRunColor = null; + aRun.Paint( + (Graphics2D)aGraphics, + nX, + aLine.GetBottom(), + aRunColor); + + nX += aRun.GetWidth(); + } + } + + aGraphics.setColor(maSeparatorColor); + + final int nTop = aGraphics.getClipBounds().y; + final int nBottom = aGraphics.getClipBounds().y+aGraphics.getClipBounds().height; + aGraphics.drawLine( + mnBarPosition0, + nTop, + mnBarPosition0, + nBottom); + aGraphics.drawLine( + mnBarPosition1, + nTop, + mnBarPosition1, + nBottom); + } + + + + + /** Paint a line with a highlight. + * There are different kinds of highlight: + * - the current line + * - one of three groups of enclosing parent elements + */ + private void PaintLineHighlight ( + final Graphics2D aG2, + final Line<TokenType> aLine) + { + final Color aBackgroundColor; + if (aLine == maHighlightedLine) + aBackgroundColor = maHighlightColor; + else + aBackgroundColor = maBackgroundColor; + + final Color aBarColor; + if (maLines.IsLineInGroup(aLine, maHighlightedGroup0)) + aBarColor = maGroupHighlightColor0; + else if (maLines.IsLineInGroup(aLine, maHighlightedGroup1)) + aBarColor = maGroupHighlightColor1; + else if (maLines.IsLineInGroup(aLine, maHighlightedGroup2)) + aBarColor = maGroupHighlightColor2; + else + aBarColor = maBackgroundColor; + + aG2.setColor(aBarColor); + aG2.fillRect( + 0, + aLine.GetTop(), + mnLeftBarWidth, + aLine.GetHeight()); + + aG2.setColor(aBackgroundColor); + aG2.fillRect( + mnLeftBarWidth, + aLine.GetTop(), + getWidth() - mnLeftBarWidth, + aLine.GetHeight()); + } + + + + + private void PaintLineNumber ( + final Graphics2D aG2, + final Line<TokenType> aLine) + { + final String sNumber = Integer.toString(aLine.GetStartOffset()); + final FontMetrics aMetrics = aG2.getFontMetrics(); + final int nWidth = aMetrics.stringWidth(sNumber); + final int nHeight = aMetrics.getHeight(); + + aG2.setColor(maLineNumberColor); + aG2.setFont(maLineNumberFont); + aG2.drawString( + sNumber, + mnBarPosition0+1 + mnNumberBarWidth-nWidth, + aLine.GetBottom() - (aLine.GetHeight()-nHeight)/2 - aMetrics.getDescent()); + } + + + + + @Override + public void mouseDragged (final MouseEvent aEvent) + { + } + + + + + @Override + public void mouseMoved (final MouseEvent aEvent) + { + final Line<TokenType> aLine = maLines.GetLineForY(aEvent.getY()); + if (aLine != null) + { + UpdateHighlightedLine(aLine); + final Run<TokenType> aRun = aLine.GetRunForX(aEvent.getX() - mnTextStart); + SetRunUnderMouse(aRun); + } + } + + + + + private void UpdateHighlightedLine (final Line<TokenType> aLine) + { + HighlightLine(aLine); + + final Iterator<Run<TokenType>> aRunIterator = aLine.iterator(); + if (aRunIterator.hasNext()) + { + final Run<TokenType> aRun = aRunIterator.next(); + if (aRun.IsGroup()) + HighlightGroup(aRun); + else + HighlightGroup(aRun.GetParent()); + } + } + + + + + @Override + public void RequestRepaint() + { + repaint(); + } + + + + + public DocumentFactory<TokenType> GetDocumentFactory() + { + return new DocumentFactory<TokenType>(maLines, this); + } + + + + + private void HighlightLine (final Line<TokenType> aLine) + { + if (aLine != maHighlightedLine) + { + maHighlightedLine = aLine; + repaint(); + } + } + + + + + private void HighlightGroup (final Run<TokenType> aRun) + { + if (maHighlightedGroup0 != aRun) + { + maHighlightedGroup0 = aRun; + + if (aRun != null) + { + final Run<TokenType> aGroup1 = aRun.GetParent(); + maHighlightedGroup1 = aGroup1; + + if (aGroup1 != null) + { + final Run<TokenType> aGroup2 = aGroup1.GetParent(); + maHighlightedGroup2 = aGroup2; + } + } + repaint(); + } + } + + + + + @Override + public void componentHidden(ComponentEvent e) + { + } + + + + + @Override + public void componentMoved (final ComponentEvent aEvent) + { + final Point aPoint = getMousePosition(); + if (aPoint != null) + UpdateHighlightedLine(maLines.GetLineForY(aPoint.y)); + } + + + + + @Override + public void componentResized(ComponentEvent e) + { + } + + + + + @Override + public void componentShown(ComponentEvent e) + { + } + + + + + public Run<TokenType> GetRun (final int nOffset) + { + final Line<TokenType> aLine = maLines.GetLineForOffset(nOffset); + if (aLine != null) + return aLine.GetRunForOffset(nOffset); + else + return null; + } + + + + + /** Return all runs that completely or partially lie in the range from + * start offset (including) and end offset (excluding). + */ + public RunRange<TokenType> GetRuns (final int nStartOffset, final int nEndOffset) + { + final Vector<Run<TokenType>> aRuns = new Vector<>(); + + for (final Line<TokenType> aLine : maLines.GetLinesForOffsets(nStartOffset, nEndOffset)) + for (final Run<TokenType> aRun : aLine.GetRunsForOffsets(nStartOffset, nEndOffset)) + aRuns.add(aRun); + + return new RunRange<TokenType>(aRuns); + } + + + + + public void MarkError (final Run<TokenType> aRun) + { + maHighlightedErrorRun = aRun; + repaint(); + } + + + + + public void ShowRun (final Run<TokenType> aRun) + { + final Container aComponent = getParent().getParent(); + if (aComponent instanceof JScrollPane) + ((JScrollPane)aComponent).getVerticalScrollBar().setValue( + aRun.GetLine().GetTop()); + } + + + + + private void SetRunUnderMouse (final Run<TokenType> aRun) + { + if (maRunUnderMouse != aRun) + { + maRunUnderMouse = aRun; + if (maRunUnderMouse != null) + setToolTipText(maRunUnderMouse.GetToolTipText()); + else + setToolTipText(null); + repaint(); + } + } + + + + + private final LineContainer<TokenType> maLines; + private final Formatter<TokenType> maFormatter; + private Line<TokenType> maHighlightedLine; + private Run<TokenType> maHighlightedGroup0; + private Run<TokenType> maHighlightedGroup1; + private Run<TokenType> maHighlightedGroup2; + private Run<TokenType> maHighlightedErrorRun; + private Run<TokenType> maRunUnderMouse; + + private final static int mnLeftBarWidth = 10; + private final static int mnNumberBarWidth = 30; + private final static int mnBarPosition0 = mnLeftBarWidth; + private final static int mnBarPosition1 = mnBarPosition0 + mnNumberBarWidth ; + private final static int mnTextStart = mnBarPosition1 + 2; + private final static Color maSeparatorColor = Color.GRAY; + private final static Color maBackgroundColor = Color.WHITE; + private final static Color maHighlightColor = new Color(0xB0E0E6); // Powder Blue + private final static Color maGroupHighlightColor0 = new Color(0x32CD32); // Lime Green + private final static Color maGroupHighlightColor1 = new Color(0x90EE90); // Light Green + private final static Color maGroupHighlightColor2 = new Color(0xbbFfbb); + private final static Color maErrorHighlightColor = new Color(0xff3020); + private final static Color maLineNumberColor = new Color(0x808080); + private final static Color maRunUnderMouseColor = maGroupHighlightColor2; + private final static Font maLineNumberFont = UIManager.getDefaults().getFont("TextField.font").deriveFont(9.0f); + +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/DocumentTokenFormatter.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/DocumentTokenFormatter.java new file mode 100755 index 000000000000..e01f78afcac0 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/DocumentTokenFormatter.java @@ -0,0 +1,399 @@ +/************************************************************** +* +* 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.viewer.xml; + +import java.awt.Color; +import java.util.HashMap; +import java.util.Map; + +import org.apache.openoffice.ooxml.viewer.tokenview.DocumentFactory; +import org.apache.openoffice.ooxml.viewer.tokenview.Style; +import org.apache.openoffice.ooxml.viewer.tokenview.TokenView; + +public class DocumentTokenFormatter +{ + DocumentTokenFormatter ( + final XMLScanner aScanner, + final TokenView<TokenType> aView) + { + maScanner = aScanner; + maView = aView.GetDocumentFactory(); + + maTagStartEndStyle = new Style().SetForegroundColor(new Color(0x87CEFA)); // Light Sky Blue + maNamespaceNameStyle = new Style().SetForegroundColor(new Color(0x7B68EE)); // Medium Slate Blue + maIdentifierStyle = new Style() + .SetForegroundColor(new Color(0x7B68EE)) // Medium Slate Blue + .SetBold(); + maTextStyle = new Style().SetForegroundColor(new Color(0xF08080)); // Light Coral + maAttributeValueStyle = new Style().SetForegroundColor(new Color(0xFFA07A)); // Light Salmon + + msIndentation = ""; + + maNamespaceMap = new HashMap<String,String>(); + } + + + + + public void Parse () + { + try + { + ParseIntro(); + AppendText("\n", TokenType.WHITESPACE, null, -1); + while (true) + { + switch (maScanner.Peek().Type) + { + case EOF: + return; + + case TAG_START: + case END_TAG_START: + ParseTag(); + AppendText("\n", TokenType.WHITESPACE, null, -1); + break; + + default: + ParseText(); + } + } + } + catch(final Exception aException) + { + aException.printStackTrace(); + } + maView.FinishText(); + } + + + + + Map<String,String> GetNamespaceMap () + { + return maNamespaceMap; + } + + + + + private void ParseIntro () + { + final Token aStartToken = maScanner.Next(); + ExpectToken(aStartToken, TokenType.INTRO_START); + ShowToken(aStartToken); + + ParseTagContent(); + + final Token aEndToken = maScanner.Next(); + ExpectToken(aEndToken, TokenType.INTRO_END); + ShowToken(aEndToken); + } + + + + + private void ParseTag () + { + final Token aStartToken = maScanner.Next(); + ExpectToken(aStartToken, TokenType.TAG_START, TokenType.END_TAG_START); + if (aStartToken.Type == TokenType.TAG_START) + maView.BeginGroup(); + if (aStartToken.Type == TokenType.END_TAG_START) + DecreaseIndentation(); + ShowToken(aStartToken); + + ParseTagContent(); + + final Token aEndToken = maScanner.Next(); + if (aStartToken.Type == TokenType.TAG_START) + ExpectToken(aEndToken, TokenType.TAG_END, TokenType.ELEMENT_END); + else + ExpectToken(aEndToken, TokenType.TAG_END); + ShowToken(aEndToken); + + if (aStartToken.Type != TokenType.END_TAG_START + && aEndToken.Type != TokenType.ELEMENT_END) + { + IncreaseIndentation(); + } + else + { + maView.EndGroup(); + } + } + + + + + private void ParseTagContent () + { + ParseQualifiedName(); + + if (maScanner.Peek().Type != TokenType.IDENTIFIER) + return; + + IncreaseIndentation(); + while (true) + { + final Token aToken = maScanner.Peek(); + if (aToken.Type != TokenType.IDENTIFIER) + break; + + if (mbStartNewLineBeforeEachAttribute + || mbStartNewLineBeforeNamespaceDefinition && aToken.Text.startsWith("xmlns")) + { + AppendText("\n", TokenType.WHITESPACE, null, -1); + AppendText(" ", TokenType.WHITESPACE, null, -1); + } + else + { + AppendText(" ", TokenType.WHITESPACE, null, -1); + } + + ParseQualifiedName(); + final Token aAssignToken = maScanner.Next(); + ExpectToken(aAssignToken, TokenType.ATTRIBUTE_DEFINE); + ShowToken(aAssignToken); + + final Token aValueToken = maScanner.Next(); + ExpectToken(aValueToken, TokenType.ATTRIBUTE_VALUE); + ShowToken(aValueToken, maAttributeValueStyle); + + if (msLastNamespaceName.equals("xmlns")) + SaveNamespaceDefinition(msLastName, StripValueQuotes(aValueToken.Text)); + } + DecreaseIndentation(); + } + + + + + private void ParseQualifiedName () + { + final Token aNameToken = maScanner.Next(); + ExpectToken(aNameToken, TokenType.IDENTIFIER); + if (maScanner.Peek().Type == TokenType.COLON) + { + final Token aSeparatorToken = maScanner.Next(); + final Token aSecondNameToken = maScanner.Next(); + ExpectToken(aSecondNameToken, TokenType.IDENTIFIER); + ShowToken(aNameToken, maNamespaceNameStyle); + ShowToken(aSeparatorToken); + ShowToken(aSecondNameToken, maIdentifierStyle); + + msLastNamespaceName = aNameToken.Text; + msLastName = aSecondNameToken.Text; + } + else + { + ShowToken(aNameToken, maIdentifierStyle); + + msLastNamespaceName = ""; + msLastName = aNameToken.Text; + } + } + + + + + private void ParseText () + { + final Token aTextToken = maScanner.Next(); + ExpectToken(aTextToken, TokenType.TEXT); + ShowToken(aTextToken, maTextStyle); + AppendText("\n", TokenType.WHITESPACE, null, -1); + } + + + + + private TokenType ExpectToken (final Token aToken, final TokenType ... aExcpectedTypes) + { + for (final TokenType eType : aExcpectedTypes) + if (aToken.Type == eType) + return eType; + + if (aExcpectedTypes.length == 1) + { + throw new RuntimeException( + String.format( + "expected '%s' but got %s", + aExcpectedTypes[0].toString(), + aToken.toString())); + } + else + { + String sList = null; + for (final TokenType eType : aExcpectedTypes) + { + if (sList != null) + sList += String.format(", '%s'", eType.toString()); + else + sList = String.format("'%s'", eType.toString()); + } + throw new RuntimeException( + String.format( + "expected one of %s but got %s", + sList, + aToken.toString())); + } + } + + + + + private void ShowToken (final Token aToken) + { + AppendText(aToken.Text, aToken.Type, GetStyle(aToken.Type), aToken.Offset); + } + + + + + private void ShowToken ( + final Token aToken, + final Style aStyle) + { + AppendText(aToken.Text, aToken.Type, aStyle, aToken.Offset); + } + + + + + private void AppendText ( + final String sText, + final TokenType eTokenType, + final Style aStyle, + final int nOffset) + { + try + { + if (mbIsAtBeginningOfLine) + { + AddText(msIndentation, TokenType.WHITESPACE, aStyle, -1); + mbIsAtBeginningOfLine = false; + } + AddText(sText, eTokenType, aStyle, nOffset); + mbIsAtBeginningOfLine = sText.endsWith("\n"); + } + catch (RuntimeException e) + { + e.printStackTrace(); + } + } + + + + + private void AddText ( + final String sText, + final TokenType eTokenType, + final Style aStyle, + final int nOffset) + { + maView.AddText(sText, eTokenType, aStyle, nOffset); + } + + + + + private void IncreaseIndentation () + { + msIndentation += " "; + } + + + + + private void DecreaseIndentation () + { + if ( ! msIndentation.isEmpty()) + msIndentation = msIndentation.substring(4); + } + + + + + private Style GetStyle (final TokenType eType) + { + switch(eType) + { + case TAG_START: + case TAG_END: + case END_TAG_START: + case INTRO_START: + case INTRO_END: + case ELEMENT_END: + return maTagStartEndStyle; + + case IDENTIFIER: + return maIdentifierStyle; + + case TEXT: + return maTextStyle; + + case ATTRIBUTE_VALUE: + return maAttributeValueStyle; + + default: + return null; + } + } + + + + + private String StripValueQuotes (final String sQuotedValue) + { + final String sValue = sQuotedValue.substring(1, sQuotedValue.length()-1); + return sValue; + } + + + + + private void SaveNamespaceDefinition (final String sShortName, final String sLongName) + { + maNamespaceMap.put(sShortName, sLongName); + } + + + + + private final XMLScanner maScanner; + private final DocumentFactory<TokenType> maView; + private final Style maTagStartEndStyle; + private final Style maNamespaceNameStyle; + private final Style maIdentifierStyle; + private final Style maTextStyle; + private final Style maAttributeValueStyle; + private String msIndentation; + private boolean mbIsAtBeginningOfLine; + + private String msLastNamespaceName; + private String msLastName; + private Map<String,String> maNamespaceMap; + + private final boolean mbStartNewLineBeforeEachAttribute = false; + private final boolean mbStartNewLineBeforeNamespaceDefinition = true; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/Token.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/Token.java new file mode 100755 index 000000000000..6919e8d0957b --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/Token.java @@ -0,0 +1,51 @@ +/************************************************************** +* +* 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.viewer.xml; + +public class Token +{ + Token ( + final TokenType eType, + final String sText, + final int nOffset) + { + Type = eType; + Text = sText; + Offset = nOffset; + } + + + + + @Override + public String toString () + { + return String.format("%s(%s)", Type.toString(), Text); + } + + + + + public final TokenType Type; + public final String Text; + public final int Offset; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/TokenType.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/TokenType.java new file mode 100755 index 000000000000..ea2c82277dde --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/TokenType.java @@ -0,0 +1,41 @@ +/************************************************************** +* +* 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.viewer.xml; + +public enum TokenType +{ + TAG_START, + TAG_END, + END_TAG_START, + INTRO_START, + INTRO_END, + IDENTIFIER, + ELEMENT_END, + ATTRIBUTE_DEFINE, + ATTRIBUTE_VALUE, + COLON, + COMMENT_START, + COMMENT_END, + TEXT, + WHITESPACE, + EOF +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/XMLScanner.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/XMLScanner.java new file mode 100755 index 000000000000..7e83c2258492 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/XMLScanner.java @@ -0,0 +1,438 @@ +/************************************************************** +* +* 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.viewer.xml; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.Vector; + +public class XMLScanner +{ + XMLScanner (final InputStream aIn) + { + Reader aReader = null; + try + { + aReader = new InputStreamReader(aIn, "UTF8"); + } + catch (UnsupportedEncodingException e) + { + e.printStackTrace(); + } + maIn = aReader; + mnNextCharacter = 0; + maTokens = new Vector<Token>(); + mnTokensReadIndex = 0; + mbIsInsideTag = false; + maTextBuffer = new int[1024]; + } + + + + + public Token Next () + { + while (maTokens.isEmpty()) + ProvideToken(); + + final Token aToken = maTokens.get(mnTokensReadIndex); + ++mnTokensReadIndex; + if (mnTokensReadIndex >= maTokens.size()) + { + maTokens.clear(); + mnTokensReadIndex = 0; + } + return aToken; + } + + + + + public Token Peek() + { + while (maTokens.isEmpty()) + ProvideToken(); + + return maTokens.get(mnTokensReadIndex); + } + + + + + private void ProvideToken () + { + final int nC = PeekCharacter(); + if (nC == -1) + { + AddToken(TokenType.EOF, "", mnOffset); + } + else if (mbIsInsideTag) + { + switch (Character.getType(nC)) + { + case Character.DIRECTIONALITY_WHITESPACE: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + ScanWhitespace(); + break; + + default: + switch(nC) + { + case '?': + case '/': + case '>': + case '=': + case ':': + case '-': + switch(ScanSymbol()) + { + case TAG_END: + case INTRO_END: + case ELEMENT_END: + mbIsInsideTag = false; + break; + default: + break; + } + break; + + case '"': + ScanQuotedValue(); + break; + + default: + ScanIdentifier(); + break; + } + } + } + else + { + switch (Character.getType(PeekCharacter())) + { + case Character.DIRECTIONALITY_WHITESPACE: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + ScanWhitespace(); + break; + + default: + if (nC == '<') + { + mbIsInsideTag = true; + ScanSymbol(); + } + else + { + ScanText(); + } + break; + } + } + } + + + + + Token NextNonWhitespaceToken () + { + while(true) + { + final Token aToken = Next(); + if (aToken.Type != TokenType.WHITESPACE) + return aToken; + } + } + + + + + private TokenType ScanSymbol () + { + final int nStartOffset = mnOffset; + + switch (PeekCharacter()) + { + case -1: + AddToken(TokenType.EOF, "", nStartOffset); + return TokenType.EOF; + + case '<': + GetNextCharacter(); + switch(PeekCharacter()) + { + case '/': + GetNextCharacter(); + AddToken(TokenType.END_TAG_START, "</", nStartOffset); + break; + + case '?': + GetNextCharacter(); + AddToken(TokenType.INTRO_START, "<?", nStartOffset); + break; + + case '!': + GetNextCharacter(); + if (GetNextCharacter() != '-') + throw new RuntimeException("expected '-' after '<!'"); + if (GetNextCharacter() != '-') + throw new RuntimeException("expected '-' after '<!-'"); + AddToken(TokenType.COMMENT_START, "<!--", nStartOffset); + break; + + default: + AddToken(TokenType.TAG_START, "<", nStartOffset); + break; + } + return maTokens.lastElement().Type; + + case '>': + GetNextCharacter(); + AddToken(TokenType.TAG_END, ">", nStartOffset); + return TokenType.TAG_END; + + case '/': + GetNextCharacter(); + if (GetNextCharacter() != '>') + throw new RuntimeException("expected '>' after '/'"); + AddToken(TokenType.ELEMENT_END, "/>", nStartOffset); + return TokenType.ELEMENT_END; + + case '?': + GetNextCharacter(); + if (GetNextCharacter() != '>') + throw new RuntimeException("expected '>' after '?'"); + AddToken(TokenType.INTRO_END, "?>", nStartOffset); + return TokenType.INTRO_END; + + case '-': + GetNextCharacter(); + if (GetNextCharacter() != '-') + throw new RuntimeException("expected '-' after '-'"); + if (GetNextCharacter() != '>') + throw new RuntimeException("expected '>' after '--'"); + AddToken(TokenType.COMMENT_END, "-->", nStartOffset); + return TokenType.COMMENT_END; + + case '=': + GetNextCharacter(); + AddToken(TokenType.ATTRIBUTE_DEFINE, "=", nStartOffset); + return TokenType.ATTRIBUTE_DEFINE; + + case ':': + GetNextCharacter(); + AddToken(TokenType.COLON, ":", nStartOffset); + return TokenType.COLON; + + default: + throw new RuntimeException(String.format( + "unexpected character '%c' of type %d", + PeekCharacter(), + Character.getType(PeekCharacter()))); + } + } + + + + + private boolean ScanIdentifier () + { + final int nStartOffset = mnOffset; + int nBufferWriteIndex = 0; + + while (true) + { + switch(Character.getType(PeekCharacter())) + { + default: + case -1: + if (nBufferWriteIndex == 0) + throw new RuntimeException( + String.format( + "missing identifier, got '%c' of type %d", + PeekCharacter(), + Character.getType(PeekCharacter()))); + AddToken( + TokenType.IDENTIFIER, + new String(maTextBuffer, 0, nBufferWriteIndex), + nStartOffset); + return true; + + case Character.LOWERCASE_LETTER: + case Character.UPPERCASE_LETTER: + case Character.DECIMAL_DIGIT_NUMBER: + if (nBufferWriteIndex >= maTextBuffer.length) + maTextBuffer = Arrays.copyOf(maTextBuffer, maTextBuffer.length*2); + maTextBuffer[nBufferWriteIndex] = GetNextCharacter(); + ++nBufferWriteIndex; + break; + } + } + } + + + + + private void ScanWhitespace () + { + final StringBuffer aBuffer = new StringBuffer(); + final int nStartOffset = mnOffset; + + while (true) + { + switch(Character.getType(PeekCharacter())) + { + default: + if (aBuffer.length() > 0) + AddToken(TokenType.WHITESPACE, aBuffer.toString(), nStartOffset); + return; + + case -1: + AddToken(TokenType.WHITESPACE, aBuffer.toString(), nStartOffset); + AddToken(TokenType.EOF, "", nStartOffset); + return; + + case Character.DIRECTIONALITY_WHITESPACE: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + aBuffer.append((char)GetNextCharacter()); + break; + } + } + } + + + + + private void ScanQuotedValue () + { + if (PeekCharacter() == '"') + { + final int nStartOffset = mnOffset; + int nBufferWriteIndex = 0; + maTextBuffer[nBufferWriteIndex++] = GetNextCharacter(); + + while (PeekCharacter() != '"') + { + // Make sure that there is enough space for this character and the end quote. + if (nBufferWriteIndex >= maTextBuffer.length-1) + maTextBuffer = Arrays.copyOf(maTextBuffer, maTextBuffer.length*2); + maTextBuffer[nBufferWriteIndex++] = GetNextCharacter(); + } + + maTextBuffer[nBufferWriteIndex++] = GetNextCharacter(); + + AddToken(TokenType.ATTRIBUTE_VALUE, new String(maTextBuffer, 0, nBufferWriteIndex), nStartOffset); + } + } + + + + + private void ScanText () + { + final int nStartOffset = mnOffset; + int nBufferWriteIndex = 0; + maTextBuffer[nBufferWriteIndex++] = GetNextCharacter(); + + while (PeekCharacter() != '<') + { + if (nBufferWriteIndex >= maTextBuffer.length) + maTextBuffer = Arrays.copyOf(maTextBuffer, maTextBuffer.length*2); + maTextBuffer[nBufferWriteIndex++] = GetNextCharacter(); + } + + AddToken(TokenType.TEXT, new String(maTextBuffer, 0, nBufferWriteIndex), nStartOffset); + } + + + + + private int GetNextCharacter () + { + final int nC; + if (mnNextCharacter != 0) + { + nC = mnNextCharacter; + mnNextCharacter = 0; + } + else + { + try + { + nC = maIn.read(); + } + catch (Exception e) + { + e.printStackTrace(); + return -1; + } + } + ++mnOffset; + return nC; + } + + + + + private int PeekCharacter () + { + if (mnNextCharacter == 0) + { + try + { + mnNextCharacter = maIn.read(); + } + catch (IOException e) + { + e.printStackTrace(); + mnNextCharacter = -1; + } + } + return mnNextCharacter; + } + + + + + private void AddToken ( + final TokenType eType, + final String sText, + final int nOffset) + { + if (eType != TokenType.WHITESPACE) + maTokens.add(new Token(eType, sText, nOffset)); + } + + + + + private final Reader maIn; + private int mnNextCharacter; + private Vector<Token> maTokens; + private int mnTokensReadIndex; + private boolean mbIsInsideTag; + private int[] maTextBuffer; + private int mnOffset; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/XMLTokenViewFactory.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/XMLTokenViewFactory.java new file mode 100755 index 000000000000..cfa6d8478e58 --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xml/XMLTokenViewFactory.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.viewer.xml; + +import java.io.InputStream; + +import org.apache.openoffice.ooxml.viewer.KeyListener; +import org.apache.openoffice.ooxml.viewer.tokenview.TokenView; + +public class XMLTokenViewFactory + extends Thread +{ + public XMLTokenViewFactory () + { + maIn = null; + start(); + } + + + + + public TokenView<TokenType> Create (final InputStream aIn) + { + TokenView<TokenType> aView = null; + synchronized(this) + { + maIn = aIn; + if (maIn != null) + { + aView = new TokenView<TokenType>(); + aView.addKeyListener(new KeyListener()); + maCurrentView = aView; + + FillDocument(); + //notify(); + } + } + + return aView; + } + + + + + @Override + public void run () + { + while(true) + { + FillDocument(); + + synchronized(this) + { + if (maIn == null) + { + try + { + wait(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + } + } + + + + + private void FillDocument () + { + final InputStream aIn = maIn; + maIn = null; + if (aIn != null) + { + new DocumentTokenFormatter(new XMLScanner(aIn), maCurrentView).Parse(); + } + } + + + + + private InputStream maIn; + private TokenView<TokenType> maCurrentView; +} diff --git a/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xmltokenview/XMLViewFactory.java b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xmltokenview/XMLViewFactory.java new file mode 100755 index 000000000000..3ca361b2ad0e --- /dev/null +++ b/ooxml/source/framework/OOXMLViewer/src/org/apache/openoffice/ooxml/viewer/xmltokenview/XMLViewFactory.java @@ -0,0 +1,143 @@ +/************************************************************** +* +* 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.viewer.xmltokenview; + +import java.util.Vector; + +import javax.xml.stream.Location; + +import org.apache.openoffice.ooxml.parser.ElementContext; +import org.apache.openoffice.ooxml.parser.ParseException; +import org.apache.openoffice.ooxml.parser.Parser; +import org.apache.openoffice.ooxml.parser.action.ActionTrigger; +import org.apache.openoffice.ooxml.parser.action.IAction; +import org.apache.openoffice.ooxml.viewer.tokenview.Run; +import org.apache.openoffice.ooxml.viewer.tokenview.RunRange; +import org.apache.openoffice.ooxml.viewer.tokenview.TokenView; +import org.apache.openoffice.ooxml.viewer.xml.TokenType; + +public class XMLViewFactory +{ + public static void AddSemanticInformation( + final TokenView<TokenType> aView, + final Parser aParser, + final Vector<String> aErrorsAndWarnings) + { + aParser.GetActionManager().AddElementStartAction( + "*", + new IAction() + { + public void Run ( + final ActionTrigger eTrigger, + final ElementContext aContext, + final String sText, + final Location aStartLocation, + final Location aEndLocation) + { + final RunRange<TokenType> aRuns = aView.GetRuns( + aStartLocation.getCharacterOffset(), + aEndLocation.getCharacterOffset()); + if (aRuns.IsEmpty()) + aView.GetRuns( + aStartLocation.getCharacterOffset(), + aEndLocation.getCharacterOffset()); + + // Search for the name (including namespace prefix) of the element. + int nIndex = aRuns.FindTokens( + TokenType.TAG_START, + TokenType.IDENTIFIER, + TokenType.COLON, + TokenType.IDENTIFIER); + if (nIndex < 0) + return; + + aRuns.Get(nIndex+1).SetToolTipText(aContext.GetTypeName()); + aRuns.Get(nIndex+2).SetToolTipText(aContext.GetTypeName()); + aRuns.Get(nIndex+3).SetToolTipText(aContext.GetTypeName()); + nIndex += 4; + + // Process the attributes. + while (true) + { + final int nStartIndex = nIndex; + nIndex = aRuns.FindTokens( + nStartIndex, + TokenType.IDENTIFIER, + TokenType.COLON, + TokenType.IDENTIFIER, + TokenType.ATTRIBUTE_DEFINE); + if (nIndex >= 0) + { + final String sAttributeName = aRuns.Get(nIndex+2).GetText(); + aRuns.Get(nIndex+0).SetToolTipText("attribute define of "+sAttributeName); + aRuns.Get(nIndex+1).SetToolTipText("attribute define of "+sAttributeName); + aRuns.Get(nIndex+2).SetToolTipText("attribute define of "+sAttributeName); + nIndex += 5; + } + else + { + // Try the variant without namespace. + nIndex = aRuns.FindTokens( + nStartIndex, + TokenType.IDENTIFIER, + TokenType.ATTRIBUTE_DEFINE); + if (nIndex >= 0) + { + final String sAttributeName = aRuns.Get(nIndex).GetText(); + aRuns.Get(nIndex).SetToolTipText("attribute define of "+sAttributeName); + nIndex += 3; + } + else + { + // No more attributes. + break; + } + } + } + } + }); + try + { + aParser.Parse(); + } + catch (final ParseException aException) + { + System.err.printf("caught exception when parsing %d,%d/%d\n", + aException.Location.getLineNumber(), + aException.Location.getColumnNumber(), + aException.Location.getCharacterOffset()); + + final Run<TokenType> aRun = aView.GetRun(aException.Location.getCharacterOffset()); + if (aRun != null) + { + aView.MarkError(aRun); + aRun.SetToolTipText( + String.format( + "parse error at %d,%d/%d\n", + aException.Location.getLineNumber(), + aException.Location.getColumnNumber(), + aException.Location.getCharacterOffset())); + aView.ShowRun(aRun); + } + } + } +} 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; +} |