Coverage Report - org.galagosearch.tupleflow.execution.JobConstructor
 
Classes in this File Line Coverage Branch Coverage Complexity
JobConstructor
0%
0/62
0%
0/22
0
JobConstructor$ConnectionHandler
0%
0/53
0%
0/28
0
JobConstructor$ConnectionsHandler
0%
0/9
0%
0/2
0
JobConstructor$JobHandler
0%
0/24
0%
0/16
0
JobConstructor$MultiHandler
0%
0/11
0%
0/2
0
JobConstructor$StageConnectionsHandler
0%
0/24
0%
0/14
0
JobConstructor$StageHandler
0%
0/20
0%
0/10
0
JobConstructor$StagesHandler
0%
0/13
0%
0/6
0
JobConstructor$StepHandler
0%
0/30
0%
0/6
0
JobConstructor$StepsHandler
0%
0/23
0%
0/16
0
 
 1  
 // BSD License (http://www.galagosearch.org/license)
 2  
 package org.galagosearch.tupleflow.execution;
 3  
 
 4  
 import java.lang.reflect.Method;
 5  
 import java.util.ArrayList;
 6  
 import java.util.HashMap;
 7  
 import java.util.TreeMap;
 8  
 import java.util.regex.Matcher;
 9  
 import java.util.regex.Pattern;
 10  
 import org.galagosearch.tupleflow.Parameters.Parser;
 11  
 import org.galagosearch.tupleflow.Parameters;
 12  
 import org.galagosearch.tupleflow.TupleFlowParameters;
 13  
 import org.xml.sax.Attributes;
 14  
 import org.xml.sax.Locator;
 15  
 import org.xml.sax.SAXException;
 16  
 import org.xml.sax.SAXParseException;
 17  
 import org.xml.sax.helpers.DefaultHandler;
 18  
 
 19  
 /**
 20  
  *
 21  
  * @author trevor
 22  
  */
 23  
 public class JobConstructor extends DefaultHandler implements ErrorHandler {
 24  0
     Pattern propertyPattern = Pattern.compile("\\$\\{([^}]+)\\}");
 25  0
     HashMap<String, String> properties = new HashMap<String, String>();
 26  0
     ArrayList<String> errors = new ArrayList<String>();
 27  
     Locator locator;
 28  
     JobHandler jobHandler;
 29  
     ErrorStore store;
 30  
     String fileName;
 31  
 
 32  
     /**
 33  
      * Creates a new instance of JobConstructor
 34  
      */
 35  0
     public JobConstructor(String fileName, ErrorStore store) {
 36  0
         this.fileName = fileName;
 37  0
         this.jobHandler = new JobHandler();
 38  0
         this.store = store;
 39  0
     }
 40  
 
 41  0
     public JobConstructor(ErrorStore store) {
 42  0
         this.fileName = "none";
 43  0
         this.jobHandler = new JobHandler();
 44  0
         this.store = store;
 45  0
     }
 46  
 
 47  
     public Job getJob() {
 48  0
         return jobHandler.getJob();
 49  
     }
 50  
 
 51  
     public ErrorStore getErrorStore() {
 52  0
         return store;
 53  
     }
 54  
 
 55  
     @Override
 56  
     public void setDocumentLocator(Locator locator) {
 57  0
         this.locator = locator;
 58  0
     }
 59  
 
 60  
     public void addError(String filename, SAXParseException exception) {
 61  0
         store.addError(new FileLocation(filename, exception.getLineNumber(), exception.
 62  
                                         getColumnNumber()), exception.getMessage());
 63  0
     }
 64  
 
 65  
     public void addError(String errorString) {
 66  0
         store.addError(location(), errorString);
 67  0
     }
 68  
 
 69  
     public void addWarning(String warning) {
 70  0
         store.addWarning(location(), warning);
 71  0
     }
 72  
 
 73  
     @Override
 74  
     public void endElement(String uri, String localName, String qName) throws SAXException {
 75  0
         jobHandler.endElement(uri, localName, qName);
 76  0
     }
 77  
 
 78  
     @Override
 79  
     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 80  0
         jobHandler.startElement(uri, localName, qName, attributes);
 81  0
     }
 82  
 
 83  
     private void verifyData(final Attributes attributes) {
 84  0
         String className = attributes.getValue("class");
 85  0
         String order = attributes.getValue("order");
 86  0
         String type = attributes.getValue("type");
 87  0
         String hash = attributes.getValue("hash");
 88  
 
 89  0
         if (className == null) {
 90  0
             addError("The data tag requires a class argument.");
 91  0
             return;
 92  
         }
 93  
 
 94  0
         if (type != null && !(type.equals("many") || type.equals("split"))) {
 95  0
             addError("The type parameter, if specified, must be 'many' or 'split'.");
 96  0
             return;
 97  
         }
 98  
 
 99  0
         if (!Verification.isClassAvailable(className)) {
 100  0
             addError("Couldn't find class: " + className);
 101  0
             return;
 102  
         }
 103  
 
 104  0
         if (order != null && !Verification.isOrderAvailable(className, order.split(" "))) {
 105  0
             addError("Couldn't find order: " + order);
 106  
         }
 107  
 
 108  0
         if (hash != null && !Verification.isOrderAvailable(className, hash.split(" "))) {
 109  0
             addError("Couldn't find order: " + hash);
 110  
         }
 111  0
     }
 112  
 
 113  
     private void verifyStep(final Attributes attributes, final TupleFlowParameters parameters) {
 114  0
         String className = attributes.getValue("class");
 115  
 
 116  0
         if (className == null) {
 117  0
             addError("The step tag requires a class argument.");
 118  0
             return;
 119  
         }
 120  
 
 121  0
         if (!Verification.isClassAvailable(className)) {
 122  0
             addError("Couldn't find a class named " + className);
 123  
         }
 124  
 
 125  
         try {
 126  0
             Class c = Class.forName(className);
 127  0
             Class[] parameterTypes = new Class[]{TupleFlowParameters.class, ErrorHandler.class};
 128  0
             Method m = c.getDeclaredMethod("verify", parameterTypes);
 129  0
             m.invoke(null, parameters, this);
 130  0
         } catch (Exception e) {
 131  0
             addError("Exception thrown during step verification: " + className + " " + e.getCause().
 132  
                      getMessage());
 133  0
         }
 134  0
     }
 135  
 
 136  
     @Override
 137  
     public void characters(char[] buffer, int offset, int length) throws SAXException {
 138  0
         jobHandler.characters(buffer, offset, length);
 139  0
     }
 140  
 
 141  
     public FileLocation location() {
 142  0
         return new FileLocation(fileName, locator);
 143  
     }
 144  
     // Handlers section
 145  0
     public class StageHandler extends StackHandler {
 146  0
         Stage stage = new Stage();
 147  
 
 148  
         @Override
 149  
         public void startHandler(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 150  0
             if (attributes.getValue("id") == null) {
 151  0
                 addError(
 152  
                         "'id' is a required attribute of 'stage'.");
 153  
             }
 154  0
             stage.name = attributes.getValue("id");
 155  0
             stage.location = location();
 156  0
         }
 157  
 
 158  
         @Override
 159  
         public void endChild(StackHandler handler, String uri, String localName, String qName) throws SAXException {
 160  0
             if (handler instanceof StageConnectionsHandler) {
 161  0
                 stage.connections = ((StageConnectionsHandler) handler).getConnectionPoints();
 162  0
             } else if (handler instanceof StepsHandler) {
 163  0
                 stage.steps = ((StepsHandler) handler).getSteps();
 164  
             }
 165  0
         }
 166  
 
 167  
         @Override
 168  
         public void unhandledStartElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 169  0
             if (qName.equals("steps")) {
 170  0
                 addHandler(new StepsHandler(), uri, localName, qName, attributes);
 171  0
             } else if (qName.equals("connections")) {
 172  0
                 addHandler(new StageConnectionsHandler(), uri, localName, qName, attributes);
 173  
             } else {
 174  0
                 addError("Unrecognized tag: '" + qName + "', expecting 'steps' or 'connections'.");
 175  0
                 addHandler(new StackHandler()); // ignore subtags
 176  
             }
 177  0
         }
 178  
 
 179  
         private Stage getStage() {
 180  0
             return stage;
 181  
         }
 182  
     }
 183  
 
 184  0
     public class StageConnectionsHandler extends StackHandler {
 185  0
         HashMap<String, StageConnectionPoint> connectionPoints = new HashMap<String, StageConnectionPoint>();
 186  
 
 187  
         public HashMap<String, StageConnectionPoint> getConnectionPoints() {
 188  0
             return connectionPoints;
 189  
         }
 190  
 
 191  
         @Override
 192  
         public void unhandledStartElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 193  0
             String id = attributes.getValue("id");
 194  0
             String clazz = attributes.getValue("class");
 195  0
             String orderSpec = attributes.getValue("order");
 196  0
             String[] order = new String[0];
 197  
 
 198  0
             if (!(qName.equals("input") || qName.equals("output"))) {
 199  0
                 addError("Expected 'input' or 'output', not " + qName);
 200  0
                 return;
 201  
             }
 202  
 
 203  0
             if (id == null) {
 204  0
                 addError("'id' is a required attribute of '" + qName + "'.");
 205  0
                 return;
 206  
             }
 207  
 
 208  0
             if (clazz == null) {
 209  0
                 addError("'class' is a required attribute of '" + qName + "'.");
 210  0
                 return;
 211  
             }
 212  
 
 213  0
             if (orderSpec != null) {
 214  0
                 order = orderSpec.split(" ");
 215  
             }
 216  0
             if (qName.equals("input")) {
 217  0
                 connectionPoints.put(id,
 218  
                                      new StageConnectionPoint(ConnectionPointType.Input,
 219  
                                                               id, clazz, order,
 220  
                                                               location()));
 221  0
             } else if (qName.equals("output")) {
 222  0
                 connectionPoints.put(id,
 223  
                                      new StageConnectionPoint(ConnectionPointType.Output,
 224  
                                                               id, clazz, order,
 225  
                                                               location()));
 226  
             } else {
 227  0
                 addError("Tag '" + qName + "' isn't legal in the connections section of a stage.");
 228  
             }
 229  0
         }
 230  
     }
 231  
 
 232  0
     public class JobHandler extends StackHandler {
 233  0
         Job job = new Job();
 234  
 
 235  
         @Override
 236  
         public void endChild(StackHandler handler, String uri, String localName, String qName) throws SAXException {
 237  0
             if (handler instanceof ConnectionsHandler) {
 238  0
                 job.connections = ((ConnectionsHandler) handler).getConnections();
 239  0
             } else if (handler instanceof StagesHandler) {
 240  0
                 job.stages = ((StagesHandler) handler).getStages();
 241  
             }
 242  0
         }
 243  
 
 244  
         @Override
 245  
         public void unhandledStartElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 246  0
             if (qName.equals("job")) {
 247  
                 // do nothing
 248  0
             } else if (qName.equals("connections")) {
 249  0
                 addHandler(new ConnectionsHandler(), uri, localName, qName, attributes);
 250  0
             } else if (qName.equals("stages")) {
 251  0
                 addHandler(new StagesHandler(), uri, localName, qName, attributes);
 252  0
             } else if (qName.equals("property")) {
 253  0
                 String name = attributes.getValue("name");
 254  0
                 String value = attributes.getValue("value");
 255  
 
 256  0
                 if (name == null || value == null) {
 257  0
                     addError("The 'property' tag requries 'name' and 'value' attributes.");
 258  
                 } else {
 259  0
                     job.properties.put(name, value);
 260  0
                     properties.put(name, value);
 261  
                 }
 262  0
             } else {
 263  0
                 addError("Unrecognized tag: '" + qName + "', expecting 'connections' or 'stages'.");
 264  0
                 addHandler(new StackHandler()); // ignore subtags
 265  
             }
 266  0
         }
 267  
 
 268  
         public Job getJob() {
 269  0
             return job;
 270  
         }
 271  
     }
 272  
 
 273  0
     public class StagesHandler extends StackHandler {
 274  0
         TreeMap<String, Stage> stages = new TreeMap<String, Stage>();
 275  
 
 276  
         @Override
 277  
         public void endChild(StackHandler handler, String uri, String localName, String qName) throws SAXException {
 278  0
             if (handler instanceof StageHandler) {
 279  0
                 Stage s = ((StageHandler) handler).getStage();
 280  0
                 if (s.name != null) {
 281  0
                     stages.put(s.name, s);
 282  
                 }
 283  
             }
 284  0
         }
 285  
 
 286  
         @Override
 287  
         public void unhandledStartElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 288  0
             if (qName.equals("stage")) {
 289  0
                 addHandler(new StageHandler(), uri, localName, qName, attributes);
 290  
             } else {
 291  0
                 addError("Unrecognized tag: '" + qName + "', expecting 'stage'.");
 292  0
                 addHandler(new StackHandler()); // ignore subtags
 293  
             }
 294  0
         }
 295  
 
 296  
         private TreeMap<String, Stage> getStages() {
 297  0
             return stages;
 298  
         }
 299  
     }
 300  
 
 301  0
     public class StepsHandler extends StackHandler {
 302  0
         ArrayList<Step> steps = new ArrayList<Step>();
 303  
 
 304  
         public ArrayList<Step> getSteps() {
 305  0
             return steps;
 306  
         }
 307  
 
 308  
         @Override
 309  
         public void endChild(StackHandler handler, String uri, String localName, String qName) throws SAXException {
 310  0
             if (handler instanceof StepHandler) {
 311  0
                 steps.add(((StepHandler) handler).getStep());
 312  0
             } else if (handler instanceof MultiHandler) {
 313  0
                 steps.add(((MultiHandler) handler).getStep());
 314  
             } else {
 315  0
                 addError("Unknown handler type: " + handler.getClass().toString());
 316  
             }
 317  0
         }
 318  
 
 319  
         @Override
 320  
         public void unhandledStartElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 321  0
             if (qName.equals("step")) {
 322  0
                 addHandler(new StepHandler(), uri, localName, qName, attributes);
 323  0
             } else if (qName.equals("multi")) {
 324  0
                 addHandler(new MultiHandler(), uri, localName, qName, attributes);
 325  0
             } else if (qName.equals("output")) {
 326  0
                 if (attributes.getValue("id") == null) {
 327  0
                     addError(
 328  
                             "'output' requires an 'id' attribute.");
 329  
                 }
 330  0
                 steps.add(new OutputStep(location(), attributes.getValue("id")));
 331  0
             } else if (qName.equals("input")) {
 332  0
                 if (attributes.getValue("id") == null) {
 333  0
                     addError(
 334  
                             "'input' requires an 'id' attribute.");
 335  
                 }
 336  0
                 steps.add(new InputStep(location(), attributes.getValue("id")));
 337  
             } else {
 338  0
                 addError(
 339  
                         "Found '" + qName + "', but was expecting 'step', 'multi', 'input' or 'output'.");
 340  
             }
 341  0
         }
 342  
     }
 343  
 
 344  
     public class StepHandler extends StackHandler {
 345  
         Step step;
 346  0
         Parameters parameters = new Parameters();
 347  
         Parser parametersHandler;
 348  
 
 349  0
         public StepHandler() throws SAXException {
 350  0
             parametersHandler = parameters.getParseHandler();
 351  0
             parametersHandler.startElement("", "", "parameters", null);
 352  0
         }
 353  
 
 354  
         @Override
 355  
         public void startHandler(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 356  0
             String className = attributes.getValue("class");
 357  
 
 358  0
             if (className == null) {
 359  0
                 addError("'class' is a required attribute of 'step'.");
 360  
             }
 361  
 
 362  0
             step = new Step(location(), className, parameters);
 363  0
         }
 364  
 
 365  
         @Override
 366  
         public void unhandledCharacters(char[] buffer, int offset, int length) throws SAXException {
 367  0
             String item = new String(buffer, offset, length);
 368  0
             Matcher m = propertyPattern.matcher(item);
 369  0
             StringBuffer stringBuffer = new StringBuffer();
 370  
 
 371  0
             while (m.find()) {
 372  0
                 String key = m.group(1);
 373  0
                 String value = properties.get(key);
 374  
 
 375  0
                 if (value == null) {
 376  0
                     addError("Couldn't find a property named '" + key + "'");
 377  0
                     m.appendReplacement(stringBuffer, "");
 378  
                 } else {
 379  0
                     m.appendReplacement(stringBuffer, value);
 380  
                 }
 381  0
             }
 382  0
             m.appendTail(stringBuffer);
 383  
 
 384  0
             buffer = stringBuffer.toString().toCharArray();
 385  0
             parametersHandler.characters(buffer, 0, buffer.length);
 386  0
         }
 387  
 
 388  
         @Override
 389  
         public void unhandledStartElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 390  0
             parametersHandler.startElement(uri, localName, qName, attributes);
 391  0
         }
 392  
 
 393  
         @Override
 394  
         public void unhandledEndElement(String uri, String localName, String qName) throws SAXException {
 395  0
             parametersHandler.endElement(uri, localName, qName);
 396  0
         }
 397  
 
 398  
         public Step getStep() {
 399  0
             return step;
 400  
         }
 401  
     }
 402  
 
 403  0
     public class MultiHandler extends StackHandler {
 404  0
         MultiStep multi = new MultiStep();
 405  
 
 406  
         public MultiStep getStep() {
 407  0
             return multi;
 408  
         }
 409  
 
 410  
         @Override
 411  
         public void endChild(StackHandler handler, String uri, String localName, String qName) throws SAXException {
 412  0
             ArrayList<Step> steps = ((StepsHandler) handler).getSteps();
 413  0
             multi.groups.add(steps);
 414  0
         }
 415  
 
 416  
         @Override
 417  
         public void unhandledStartElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 418  0
             if (!qName.equals("group")) {
 419  0
                 addError("Found '" + qName + "' but was expecting 'group'.");
 420  0
                 return;
 421  
             }
 422  
 
 423  0
             addHandler(new StepsHandler());
 424  0
         }
 425  
     }
 426  
 
 427  0
     public class ConnectionsHandler extends StackHandler {
 428  0
         public ArrayList<Connection> connections = new ArrayList<Connection>();
 429  
 
 430  
         public ArrayList<Connection> getConnections() {
 431  0
             return connections;
 432  
         }
 433  
 
 434  
         @Override
 435  
         public void unhandledStartElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 436  0
             if (!qName.equals("connection")) {
 437  0
                 addError("Found '" + qName + "' but expected 'connection'");
 438  
             }
 439  
 
 440  0
             addHandler(new ConnectionHandler(), uri, localName, qName, attributes);
 441  0
         }
 442  
 
 443  
         @Override
 444  
         public void endChild(StackHandler handler, String uri, String localName, String qName) throws SAXException {
 445  0
             connections.add(((ConnectionHandler) handler).getConnection());
 446  0
         }
 447  
     }
 448  
 
 449  0
     public class ConnectionHandler extends StackHandler {
 450  
         public Connection connection;
 451  
 
 452  
         public Connection getConnection() {
 453  0
             return connection;
 454  
         }
 455  
 
 456  
         @Override
 457  
         public void startHandler(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 458  0
             String className = attributes.getValue("class");
 459  
 
 460  0
             if (className == null) {
 461  0
                 addError("'class' is a required attribute of a connection.");
 462  
             }
 463  
 
 464  0
             String orderSpec = attributes.getValue("order");
 465  0
             String[] order = new String[0];
 466  
 
 467  0
             if (orderSpec == null) {
 468  0
                 addError("'order' is a required attribute of a connection.");
 469  
             } else {
 470  0
                 order = orderSpec.split(" ");
 471  
             }
 472  
 
 473  0
             String hashSpec = attributes.getValue("hash");
 474  0
             String[] hash = null;
 475  
 
 476  0
             if (hashSpec != null) {
 477  0
                 hash = hashSpec.split(" ");
 478  
             }
 479  0
             String hashCount = attributes.getValue("hashCount");
 480  0
             int count = -1;
 481  
 
 482  0
             if (hashCount != null) {
 483  
                 try {
 484  0
                     count = Integer.parseInt(hashCount);
 485  0
                 } catch (NumberFormatException e) {
 486  0
                     addError(
 487  
                             "Expected a numeric argument for 'count', but saw '" + hashCount + "' instead.");
 488  0
                 }
 489  
             }
 490  
 
 491  0
             Verification.requireClass(className, JobConstructor.this);
 492  0
             connection = new Connection(location(), className, order, hash, count);
 493  0
         }
 494  
 
 495  
         @Override
 496  
         public void unhandledStartElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 497  0
             String stageName = attributes.getValue("stage");
 498  0
             String pointName = attributes.getValue("endpoint");
 499  
 
 500  0
             if (!qName.equals("input") && !qName.equals("output")) {
 501  0
                 addError("Found '" + qName + "' but expected 'input' or 'output'.");
 502  0
                 return;
 503  
             }
 504  
 
 505  0
             if (stageName == null) {
 506  0
                 addError("'stage' is a required attribute of '" + qName + "'.");
 507  0
                 return;
 508  
             }
 509  
 
 510  0
             if (pointName == null) {
 511  0
                 addError("'endpoint' is a required attribute of '" + qName + "'.");
 512  0
                 return;
 513  
             }
 514  
 
 515  0
             if (qName.equals("input")) {
 516  0
                 connection.inputs.add(new ConnectionEndPoint(location(), stageName, pointName,
 517  
                                                              ConnectionPointType.Input));
 518  0
             } else if (qName.equals("output")) {
 519  0
                 String assignmentString = attributes.getValue("assignment");
 520  
 
 521  0
                 if (assignmentString == null) {
 522  0
                     addError("'assignment' is a required attribute of 'output'.");
 523  0
                     return;
 524  
                 }
 525  
 
 526  
                 ConnectionAssignmentType assignment;
 527  
 
 528  0
                 if (assignmentString.equals("one")) {
 529  0
                     assignment = ConnectionAssignmentType.One;
 530  0
                 } else if (assignmentString.equals("each")) {
 531  0
                     assignment = ConnectionAssignmentType.Each;
 532  0
                 } else if (assignmentString.equals("combined")) {
 533  0
                     assignment = ConnectionAssignmentType.Combined;
 534  
                 } else {
 535  0
                     addError("'assignment' needs to be either 'one', 'each', or 'combined' (not '" +
 536  
                              assignmentString + "').");
 537  0
                     return;
 538  
                 }
 539  
 
 540  0
                 ConnectionEndPoint point = new ConnectionEndPoint(location(),
 541  
                                                                   stageName,
 542  
                                                                   pointName,
 543  
                                                                   assignment,
 544  
                                                                   ConnectionPointType.Output);
 545  
 
 546  0
                 connection.outputs.add(point);
 547  
             }
 548  0
         }
 549  
     }
 550  
 }