1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 package org.apache.openoffice.ooxml.parser; 23 24 import java.io.File; 25 import java.util.Stack; 26 import java.util.Vector; 27 28 import javax.xml.stream.Location; 29 30 import org.apache.openoffice.ooxml.parser.action.ActionManager; 31 import org.apache.openoffice.ooxml.parser.action.ActionTrigger; 32 import org.apache.openoffice.ooxml.parser.action.IAction; 33 import org.apache.openoffice.ooxml.parser.attribute.AttributeManager; 34 import org.apache.openoffice.ooxml.parser.attribute.AttributeProvider; 35 import org.apache.openoffice.ooxml.parser.attribute.AttributeValues; 36 import org.apache.openoffice.ooxml.parser.type.SimpleTypeManager; 37 38 /** The state machine is initialized at creation from the data tables 39 * created previously by a stack automaton. 40 */ 41 public class StateMachine 42 { StateMachine( final File aParseTableFile, final Vector<String> aErrorsAndWarnings)43 public StateMachine ( 44 final File aParseTableFile, 45 final Vector<String> aErrorsAndWarnings) 46 { 47 if (Log.Dbg != null) 48 Log.Dbg.printf("reading parse tables from %s\n", aParseTableFile.toString()); 49 50 final ParseTableReader aReader = new ParseTableReader(aParseTableFile); 51 maNamespaceMap = new NamespaceMap(aReader.GetSection("namespace")); 52 maNameMap = new NameMap(aReader.GetSection("name")); 53 maStateNameMap = new NameMap(aReader.GetSection("state-name")); 54 maTransitions = new TransitionTable(aReader.GetSection("transition")); 55 maSkipStates = new SkipStateTable(aReader.GetSection("skip")); 56 maAttributeValueMap = new NameMap(aReader.GetSection("attribute-value")); 57 maAcceptingStates = new AcceptingStateTable(aReader.GetSection("accepting-state")); 58 maSimpleTypeManager = new SimpleTypeManager( 59 aReader.GetSection("simple-type"), 60 maAttributeValueMap); 61 maAttributeManager = new AttributeManager( 62 aReader.GetSection("attribute"), 63 maNamespaceMap, 64 maNameMap, 65 maStateNameMap, 66 maSimpleTypeManager, 67 aErrorsAndWarnings); 68 mnStartStateId = Integer.parseInt(aReader.GetSection("start-state").firstElement()[1]); 69 mnEndStateId = Integer.parseInt(aReader.GetSection("end-state").firstElement()[1]); 70 71 mnCurrentStateId = mnStartStateId; 72 maStateStack = new Stack<>(); 73 maElementContextStack = new Stack<>(); 74 maActionManager = new ActionManager(maStateNameMap); 75 maErrorsAndWarnings = aErrorsAndWarnings; 76 77 if (Log.Dbg != null) 78 { 79 Log.Dbg.printf("read %d namespace, %d names, %d states (%d skip, %d accept), %d transitions and %d attributes\n", 80 maNamespaceMap.GetNamespaceCount(), 81 maNameMap.GetNameCount(), 82 maStateNameMap.GetNameCount(), 83 maSkipStates.GetSkipStateCount(), 84 maAcceptingStates.GetAcceptingStateCount(), 85 maTransitions.GetTransitionCount(), 86 maAttributeManager.GetAttributeCount()); 87 Log.Dbg.printf("starting in state _start_ (%d)\n", mnCurrentStateId); 88 } 89 } 90 91 92 93 ProcessStartElement( final String sNamespaceURI, final String sElementName, final Location aStartLocation, final Location aEndLocation, final AttributeProvider aAttributes)94 public boolean ProcessStartElement ( 95 final String sNamespaceURI, 96 final String sElementName, 97 final Location aStartLocation, 98 final Location aEndLocation, 99 final AttributeProvider aAttributes) 100 { 101 boolean bResult = false; 102 103 try 104 { 105 final NamespaceMap.NamespaceDescriptor aNamespaceDescriptor = maNamespaceMap.GetDescriptorForURI(sNamespaceURI); 106 final int nElementNameId = maNameMap.GetIdForName(sElementName); 107 if (Log.Dbg != null) 108 Log.Dbg.printf("%s:%s(%d:%d) L%dC%d\n", 109 aNamespaceDescriptor.Prefix, 110 sElementName, 111 aNamespaceDescriptor.Id, 112 nElementNameId, 113 aStartLocation.getLineNumber(), 114 aStartLocation.getColumnNumber()); 115 116 final Transition aTransition = maTransitions.GetTransition( 117 mnCurrentStateId, 118 aNamespaceDescriptor.Id, 119 nElementNameId); 120 if (aTransition == null) 121 { 122 final String sText = String.format( 123 "can not find transition for state %s(%d) and element %s:%s(%d:%d) at L%dC%d\n", 124 maStateNameMap.GetNameForId(mnCurrentStateId), 125 mnCurrentStateId, 126 aNamespaceDescriptor.Prefix, 127 maNameMap.GetNameForId(nElementNameId), 128 aNamespaceDescriptor.Id, 129 nElementNameId, 130 aStartLocation.getLineNumber(), 131 aStartLocation.getColumnNumber()); 132 Log.Err.printf(sText); 133 if (Log.Dbg != null) 134 Log.Dbg.printf(sText); 135 } 136 else 137 { 138 if (Log.Dbg != null) 139 { 140 Log.Dbg.printf(" %s(%d) -> %s(%d) via %s(%d)", 141 maStateNameMap.GetNameForId(mnCurrentStateId), 142 mnCurrentStateId, 143 maStateNameMap.GetNameForId(aTransition.GetEndStateId()), 144 aTransition.GetEndStateId(), 145 maStateNameMap.GetNameForId(aTransition.GetActionId()), 146 aTransition.GetActionId()); 147 Log.Dbg.printf("\n"); 148 } 149 150 // Follow the transition to its end state but first process its 151 // content. We do that by 152 153 if (Log.Dbg != null) 154 Log.Dbg.IncreaseIndentation(); 155 156 // a) pushing the end state to the state stack so that on the 157 // end tag that corresponds to the current start tag it will become the current state. 158 maStateStack.push(aTransition.GetEndStateId()); 159 160 // b) entering the state that corresponds to start tag that 161 // we are currently processing. 162 mnCurrentStateId = aTransition.GetActionId(); 163 164 // c) Prepare the attributes and store them in the new element context. 165 final AttributeValues aAttributeValues = maAttributeManager.ParseAttributes( 166 mnCurrentStateId, 167 aAttributes); 168 169 // d) creating a new ElementContext for the element that just starts. 170 maElementContextStack.push(maCurrentElementContext); 171 final ElementContext aPreviousElementContext = maCurrentElementContext; 172 maCurrentElementContext = new ElementContext( 173 sElementName, 174 maStateNameMap.GetNameForId(aTransition.GetActionId()), 175 false, 176 aAttributeValues, 177 aPreviousElementContext); 178 179 // e) and run all actions that are bound to the current start tag. 180 ExecuteActions( 181 mnCurrentStateId, 182 maCurrentElementContext, 183 ActionTrigger.ElementStart, 184 null, 185 aStartLocation, 186 aEndLocation); 187 188 bResult = true; 189 } 190 } 191 catch (RuntimeException aException) 192 { 193 Log.Err.printf("error at line %d and column %d\n", 194 aStartLocation.getLineNumber(), 195 aStartLocation.getColumnNumber()); 196 throw aException; 197 } 198 return bResult; 199 } 200 201 202 203 ProcessEndElement( final String sNamespaceURI, final String sElementName, final Location aStartLocation, final Location aEndLocation)204 public void ProcessEndElement ( 205 final String sNamespaceURI, 206 final String sElementName, 207 final Location aStartLocation, 208 final Location aEndLocation) 209 { 210 if ( ! maAcceptingStates.Contains(mnCurrentStateId) 211 && mnCurrentStateId!=-1) 212 { 213 if (Log.Dbg != null) 214 Log.Dbg.printf("current state %s(%d) is not an accepting state\n", 215 maStateNameMap.GetNameForId(mnCurrentStateId), 216 mnCurrentStateId); 217 throw new RuntimeException("not expecting end element "+sElementName); 218 } 219 220 final NamespaceMap.NamespaceDescriptor aDescriptor = maNamespaceMap.GetDescriptorForURI(sNamespaceURI); 221 222 // Leave the current element. 223 224 final int nPreviousStateId = mnCurrentStateId; 225 mnCurrentStateId = maStateStack.pop(); 226 if (mnCurrentStateId == mnEndStateId) 227 mnCurrentStateId = mnStartStateId; 228 229 final ElementContext aPreviousElementContext = maCurrentElementContext; 230 maCurrentElementContext = maElementContextStack.pop(); 231 232 ExecuteActions( 233 nPreviousStateId, 234 aPreviousElementContext, 235 ActionTrigger.ElementEnd, 236 null, 237 aStartLocation, 238 aEndLocation); 239 240 if (Log.Dbg != null) 241 { 242 Log.Dbg.DecreaseIndentation(); 243 Log.Dbg.printf("/%s:%s L%d%d\n", 244 aDescriptor.Prefix, 245 sElementName, 246 aStartLocation.getLineNumber(), 247 aStartLocation.getColumnNumber()); 248 Log.Dbg.printf(" %s(%d) <- %s(%d)\n", 249 maStateNameMap.GetNameForId(nPreviousStateId), 250 nPreviousStateId, 251 maStateNameMap.GetNameForId(mnCurrentStateId), 252 mnCurrentStateId); 253 } 254 } 255 256 257 258 ProcessCharacters( final String sText, final Location aStartLocation, final Location aEndLocation)259 public void ProcessCharacters ( 260 final String sText, 261 final Location aStartLocation, 262 final Location aEndLocation) 263 { 264 if (Log.Dbg != null) 265 Log.Dbg.printf("text [%s]\n", sText.replace("\n", "\\n")); 266 267 ExecuteActions( 268 mnCurrentStateId, 269 maCurrentElementContext, 270 ActionTrigger.Text, 271 sText, 272 aStartLocation, 273 aEndLocation); 274 275 } 276 277 278 279 IsInSkipState()280 public boolean IsInSkipState () 281 { 282 return maSkipStates.Contains(mnCurrentStateId); 283 } 284 285 286 287 GetActionManager()288 public ActionManager GetActionManager () 289 { 290 return maActionManager; 291 } 292 293 294 295 ExecuteActions( final int nStateId, final ElementContext aElementContext, final ActionTrigger eTrigger, final String sText, final Location aStartLocation, final Location aEndLocation)296 private void ExecuteActions ( 297 final int nStateId, 298 final ElementContext aElementContext, 299 final ActionTrigger eTrigger, 300 final String sText, 301 final Location aStartLocation, 302 final Location aEndLocation) 303 { 304 final Iterable<IAction> aActions = maActionManager.GetActions(nStateId, eTrigger); 305 if (aActions != null) 306 for (final IAction aAction : aActions) 307 aAction.Run(eTrigger, aElementContext, sText, aStartLocation, aEndLocation); 308 } 309 310 311 312 313 private final NamespaceMap maNamespaceMap; 314 private final NameMap maNameMap; 315 private final NameMap maStateNameMap; 316 private final TransitionTable maTransitions; 317 private final SimpleTypeManager maSimpleTypeManager; 318 private final AttributeManager maAttributeManager; 319 private final NameMap maAttributeValueMap; 320 private int mnCurrentStateId; 321 private Stack<Integer> maStateStack; 322 private ElementContext maCurrentElementContext; 323 private Stack<ElementContext> maElementContextStack; 324 private final int mnStartStateId; 325 private final int mnEndStateId; 326 private SkipStateTable maSkipStates; 327 private AcceptingStateTable maAcceptingStates; 328 private final ActionManager maActionManager; 329 private final Vector<String> maErrorsAndWarnings; 330 } 331