blob: a8e660343cf831a8e9eec6feecd2fbafb5f9677d [file] [log] [blame]
/**************************************************************
*
* 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("\"", "&quot;").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;
}