Coverage Report - org.galagosearch.tupleflow.execution.Verification
 
Classes in this File Line Coverage Branch Coverage Complexity
Verification
0%
0/231
0%
0/116
0
Verification$TypeState
0%
0/23
0%
0/6
0
Verification$VerificationErrorHandler
0%
0/8
N/A
0
Verification$VerificationParameters
0%
0/28
0%
0/16
0
 
 1  
 // BSD License (http://www.galagosearch.org/license)
 2  
 package org.galagosearch.tupleflow.execution;
 3  
 
 4  
 import java.io.File;
 5  
 import java.io.IOException;
 6  
 import java.lang.reflect.InvocationTargetException;
 7  
 import java.lang.reflect.Method;
 8  
 import java.lang.reflect.Modifier;
 9  
 import java.util.ArrayList;
 10  
 import java.util.Arrays;
 11  
 import org.galagosearch.tupleflow.Counter;
 12  
 import org.galagosearch.tupleflow.InputClass;
 13  
 import org.galagosearch.tupleflow.OutputClass;
 14  
 import org.galagosearch.tupleflow.Parameters;
 15  
 import org.galagosearch.tupleflow.Processor;
 16  
 import org.galagosearch.tupleflow.TupleFlowParameters;
 17  
 import org.galagosearch.tupleflow.Type;
 18  
 import org.galagosearch.tupleflow.TypeReader;
 19  
 
 20  
 /**
 21  
  *
 22  
  * @author trevor
 23  
  */
 24  0
 public class Verification {
 25  
     private static class VerificationErrorHandler implements ErrorHandler {
 26  
         FileLocation location;
 27  
         ErrorStore store;
 28  
 
 29  0
         public VerificationErrorHandler(ErrorStore store, FileLocation location) {
 30  0
             this.location = location;
 31  0
             this.store = store;
 32  0
         }
 33  
 
 34  
         public void addWarning(String message) {
 35  0
             store.addWarning(location, message);
 36  0
         }
 37  
 
 38  
         public void addError(String message) {
 39  0
             store.addError(location, message);
 40  0
         }
 41  
     }
 42  
 
 43  
     private static class VerificationParameters implements TupleFlowParameters {
 44  
         Step step;
 45  
         Stage stage;
 46  
 
 47  0
         public VerificationParameters(Stage stage, Step step) {
 48  0
             this.stage = stage;
 49  0
             this.step = step;
 50  0
         }
 51  
 
 52  
         public Counter getCounter(String name) {
 53  0
             return null;
 54  
         }
 55  
         
 56  
         public Processor getTypeWriter(String specification) throws IOException {
 57  0
             return null;
 58  
         }
 59  
 
 60  
         public TypeReader getTypeReader(String specification) throws IOException {
 61  0
             return null;
 62  
         }
 63  
 
 64  
         public boolean writerExists(String specification, String className, String[] order) {
 65  0
             StageConnectionPoint point = stage.connections.get(specification);
 66  
 
 67  0
             if (point == null) {
 68  0
                 return false;
 69  
             }
 70  0
             if (point.type != ConnectionPointType.Output) {
 71  0
                 return false;
 72  
             }
 73  0
             if (!className.equals(point.getClassName())) {
 74  0
                 return false;
 75  
             }
 76  0
             if (!compatibleOrders(order, point.getOrder())) {
 77  0
                 return false;
 78  
             }
 79  0
             return true;
 80  
         }
 81  
 
 82  
         public boolean readerExists(String specification, String className, String[] order) {
 83  0
             StageConnectionPoint point = stage.connections.get(specification);
 84  
 
 85  0
             if (point == null) {
 86  0
                 return false;
 87  
             }
 88  0
             if (point.type != ConnectionPointType.Input) {
 89  0
                 return false;
 90  
             }
 91  0
             if (!className.equals(point.getClassName())) {
 92  0
                 return false;
 93  
             }
 94  0
             if (!compatibleOrders(point.getOrder(), order)) {
 95  0
                 return false;
 96  
             }
 97  0
             return true;
 98  
         }
 99  
 
 100  
         public Parameters getXML() {
 101  0
             return step.getParameters();
 102  
         }
 103  
     }
 104  
 
 105  
     /**
 106  
      * Tests to see if two object orders are compatible.  By compatible, we mean that
 107  
      * a list of objects in outputOrder is also in inputOrder.  This is true if the orders
 108  
      * are identical, but also if inputOrder is more permissive than outputOrder.  
 109  
      * 
 110  
      * For instance, suppose we are sorting a list of people's names.  People typically
 111  
      * have a surname (last name) and a given name (first name).  In Galago notation,
 112  
      * consider these two orders you could use:
 113  
      *      +surname
 114  
      *      +surname +givenName
 115  
      *
 116  
      * If a list is ordered by (+surname +givenName), then it is also ordered by
 117  
      * +surname.  The reverse isn't true, though: if you order by +surname, you
 118  
      * haven't necessarily ordered by (+surname +givenName).  Therefore:
 119  
      *      compatibleOrders({ "+surname" }, { "+surname", "+givenName" }) == false
 120  
      *      compatibleOrders({ "+surname", "+givenName" }, { "+surname" }) == true
 121  
      *
 122  
      * @param currentOrder  The current order of the data that is supplied.
 123  
      * @param requiredOrder The required order of the data.
 124  
      */
 125  
     public static boolean compatibleOrders(String[] currentOrder, String[] requiredOrder) {
 126  
         // if the required order is more specific than the current order, it's not compatible
 127  0
         if (currentOrder.length < requiredOrder.length) {
 128  0
             return false;        // the required order needs to agree with the current order in each case.
 129  
         }
 130  0
         for (int i = 0; i < requiredOrder.length; i++) {
 131  0
             if (!currentOrder[i].equals(requiredOrder[i])) {
 132  0
                 return false;
 133  
             }
 134  
         }
 135  
 
 136  0
         return true;
 137  
     }
 138  
 
 139  
     public static boolean requireParameters(String[] required, Parameters parameters, ErrorHandler handler) {
 140  0
         boolean result = true;
 141  0
         for (String key : required) {
 142  0
             if (!parameters.containsKey(key)) {
 143  0
                 handler.addError("The parameter '" + key + "' is required.");
 144  0
                 result = false;
 145  
             }
 146  
         }
 147  0
         return result;
 148  
     }
 149  
 
 150  
     public static boolean isOrderAvailable(String typeName, String[] orderSpec) {
 151  
         try {
 152  0
             Class typeClass = Class.forName(typeName);
 153  0
             Type type = (Type) typeClass.newInstance();
 154  0
             return type.getOrder(orderSpec) != null;
 155  0
         } catch (Exception e) {
 156  0
             return false;
 157  
         }
 158  
     }
 159  
 
 160  
     public static boolean isClassAvailable(String name) {
 161  
         try {
 162  0
             Class.forName(name);
 163  0
         } catch (Exception e) {
 164  0
             return false;
 165  0
         }
 166  
 
 167  0
         return true;
 168  
     }
 169  
 
 170  
     public static boolean requireOrder(String typeName, String[] orderSpec, ErrorHandler handler) {
 171  0
         if (!isOrderAvailable(typeName, orderSpec)) {
 172  0
             StringBuilder builder = new StringBuilder();
 173  
 
 174  0
             for (String orderKey : orderSpec) {
 175  0
                 builder.append(orderKey);
 176  
             }
 177  
 
 178  0
             handler.addError(
 179  
                     "The order '" + builder.toString() + "' was not found in " + typeName + ".");
 180  0
             return false;
 181  
         }
 182  0
         return true;
 183  
     }
 184  
 
 185  
     public static boolean requireClass(String typeName, ErrorHandler handler) {
 186  0
         if (!isClassAvailable(typeName)) {
 187  0
             handler.addError("The class '" + typeName + "' could not be found.");
 188  0
             return false;
 189  
         }
 190  0
         return true;
 191  
     }
 192  
 
 193  
     public static boolean requireWriteableFile(String pathname, ErrorHandler handler) {
 194  0
         File path = new File(pathname);
 195  
 
 196  0
         if (path.exists() && !path.isFile()) {
 197  0
             handler.addError("Pathname " + pathname + " exists already and isn't a file.");
 198  0
             return false;
 199  
         }
 200  
 
 201  0
         return requireWriteableDirectoryParent(pathname, handler);
 202  
 
 203  
     }
 204  
 
 205  
     public static boolean requireWriteableDirectory(String pathname, ErrorHandler handler) {
 206  0
         File path = new File(pathname);
 207  
 
 208  0
         if (path.isFile()) {
 209  0
             handler.addError("Pathname " + pathname + " is a file, but a directory is required.");
 210  0
             return false;
 211  
         }
 212  
 
 213  0
         if (path.isDirectory() && !path.canWrite()) {
 214  0
             handler.addError("Pathname " + pathname + " is a directory, but it isn't writable.");
 215  0
             return false;
 216  
         }
 217  
 
 218  0
         return requireWriteableDirectoryParent(pathname, handler);
 219  
     }
 220  
 
 221  
     /**
 222  
      * <p>If pathname exists, returns true.  If pathname doesn't exist, checks to
 223  
      * see if it's possible for this process to create something called pathname.</p>
 224  
      * 
 225  
      * <p>This method returns false if the closest existing parent directory of pathname
 226  
      * is not writeable (or isn't a directory)</p>
 227  
      *
 228  
      * <p>For example, if filename is /a/b/c/d/e/f, this method will return true if:
 229  
      * <ul>
 230  
      * <li>/a/b/c/d/e/f exists</li>
 231  
      * <li>/a/b/c/d/e/f doesn't exist, but /a/b/c/d/e does, and is writeable</li>
 232  
      * <li>/a/b/d/d/e doesn't exist, but /a/b/c/d does, and is writeable</li>
 233  
      * <li>/a doesn't exist, but / does, and is writeable.</li>
 234  
      * </ul>
 235  
      * </p>
 236  
      */
 237  
     public static boolean requireWriteableDirectoryParent(final String pathname, final ErrorHandler handler) {
 238  0
         File path = new File(pathname);
 239  
 
 240  0
         if (!path.exists()) {
 241  0
             String parent = path.getParent();
 242  
 
 243  0
             while (parent != null && !new File(parent).exists()) {
 244  0
                 parent = new File(parent).getParent();
 245  
             }
 246  
 
 247  0
             if (parent == null) {
 248  0
                 parent = System.getProperty("user.dir");
 249  
             }
 250  
 
 251  0
             if (!new File(parent).canWrite()) {
 252  0
                 handler.addError(
 253  
                         "Pathname " + pathname + " doesn't exist, and the parent directory isn't writable.");
 254  0
                 return false;
 255  
             }
 256  
         }
 257  
 
 258  0
         return true;
 259  
     }
 260  
 
 261  0
     private static class TypeState {
 262  
         public String className;
 263  
         public String[] order;
 264  
         public boolean defined;
 265  
 
 266  0
         public TypeState() {
 267  0
             this.className = "java.lang.Object";
 268  0
             this.order = new String[0];
 269  0
             this.defined = false;
 270  0
         }
 271  
 
 272  0
         public TypeState(TypeState state) {
 273  0
             this.className = state.getClassName();
 274  0
             this.order = state.getOrder();
 275  0
             this.defined = state.isDefined();
 276  0
         }
 277  
 
 278  
         public boolean check(String className, String[] order) {
 279  0
             if (!defined) {
 280  0
                 return true;
 281  
             }
 282  0
             return className.equals(this.className) && Verification.compatibleOrders(order,
 283  
                                                                                      this.order);
 284  
         }
 285  
 
 286  
         public String[] getOrder() {
 287  0
             return order;
 288  
         }
 289  
 
 290  
         public String getClassName() {
 291  0
             return className;
 292  
         }
 293  
 
 294  
         public void update(String className, String[] order) {
 295  0
             this.className = className;
 296  0
             this.order = order;
 297  0
             this.defined = true;
 298  0
         }
 299  
 
 300  
         public void setDefined(boolean defined) {
 301  0
             this.defined = defined;
 302  0
         }
 303  
 
 304  
         private boolean isDefined() {
 305  0
             return defined;
 306  
         }
 307  
     }
 308  
 
 309  
     public static void verify(TypeState state, Stage stage, ArrayList<Step> steps, ErrorStore store) {
 310  0
         for (int i = 0; i < steps.size(); i++) {
 311  0
             Step step = steps.get(i);
 312  0
             boolean isLastStep = (i == (steps.size() - 1));
 313  
 
 314  0
             if (step instanceof InputStep) {
 315  
                 // This step was an <input> tag
 316  0
                 InputStep input = (InputStep) step;
 317  0
                 StageConnectionPoint point = stage.connections.get(input.getId());
 318  
 
 319  0
                 if (point == null) {
 320  0
                     store.addError(step.getLocation(),
 321  
                                    "Input references a connection called '" +
 322  
                                    input.getId() + "', but it isn't listed in the connections section of the stage.");
 323  
                 } else {
 324  0
                     state.update(point.getClassName(), point.getOrder());
 325  
                 }
 326  0
             } else if (step instanceof OutputStep) {
 327  
                 // This step was an <output> tag
 328  0
                 OutputStep output = (OutputStep) step;
 329  0
                 StageConnectionPoint point = stage.connections.get(output.getId());
 330  
 
 331  0
                 if (point == null) {
 332  0
                     store.addError(step.getLocation(),
 333  
                                    "Output references a connection called '" +
 334  
                                    output.getId() + "', but it isn't listed in the connections section of the stage.");
 335  
                 } else {
 336  0
                     if (state.isDefined() && !state.getClassName().equals(point.getClassName())) {
 337  0
                         store.addError(step.getLocation(), "Previous step makes '" +
 338  
                                        state.getClassName() + "' objects, but this output connection wants '" +
 339  
                                        point.getClassName() + "' objects.");
 340  0
                     } else if (state.isDefined() && !compatibleOrders(state.getOrder(), point.
 341  
                                                                       getOrder())) {
 342  0
                         store.addError(step.getLocation(), "Previous step outputs objects in '" +
 343  
                                        Arrays.toString(state.getOrder()) + "' order, but incompatible order '" +
 344  
                                        Arrays.toString(point.getOrder()) + "' is required.");
 345  
                     }
 346  
                 }
 347  
 
 348  0
                 state.setDefined(false);
 349  0
             } else if (step instanceof MultiStep) {
 350  
                 // This is a <multi> tag.  The MultiStep object contains
 351  
                 // many different object groups.
 352  0
                 MultiStep multiStep = (MultiStep) step;
 353  
 
 354  0
                 for (ArrayList<Step> group : multiStep.groups) {
 355  0
                     verify(new TypeState(state), stage, group, store);
 356  0
                     state.setDefined(false);
 357  
                 }
 358  0
             } else {
 359  
                 Class clazz;
 360  
                 try {
 361  0
                     clazz = Class.forName(step.getClassName());
 362  0
                 } catch (ClassNotFoundException ex) {
 363  0
                     store.addError(step.getLocation(), "Couldn't find class: " + step.getClassName());
 364  0
                     continue;
 365  0
                 }
 366  
 
 367  0
                 VerificationParameters vp = new VerificationParameters(stage, step);
 368  
 
 369  0
                 verifyInputClass(state, step, clazz, vp, store);
 370  0
                 verifyStepClass(clazz, step, store, vp);
 371  
 
 372  0
                 if (!isLastStep) {
 373  0
                     verifyOutputClass(state, clazz, step, store, vp);
 374  
                 }
 375  
             }
 376  
         }
 377  0
     }
 378  
 
 379  
     private static void verifyOutputClass(TypeState state, final Class clazz, final Step step, final ErrorStore store, final VerificationParameters vp) {
 380  0
         String[] outputOrder = new String[0];
 381  0
         String outputClass = "java.lang.Object";
 382  
 
 383  
         try {
 384  0
             OutputClass outputClassAnnotation = (OutputClass) clazz.getAnnotation(OutputClass.class);
 385  
 
 386  0
             if (outputClassAnnotation != null) {
 387  0
                 outputClass = outputClassAnnotation.className();
 388  0
                 outputOrder = outputClassAnnotation.order();
 389  0
                 state.update(outputClass, outputOrder);
 390  
 
 391  0
                 if (!Verification.isClassAvailable(outputClass)) {
 392  0
                     store.addError(step.getLocation(), step.getClassName() + ": Class " + step.
 393  
                                    getClassName() + " has an " +
 394  
                                    "@OutputClass annotation with the class name '" + outputClass +
 395  
                                    "' which couldn't be found.");
 396  0
                     state.setDefined(false);
 397  
                 } else {
 398  0
                     state.update(outputClass, outputOrder);
 399  
                 }
 400  
             } else {
 401  
                 try {
 402  0
                     Method getOutputClass = clazz.getDeclaredMethod("getOutputClass",
 403  
                                                                     TupleFlowParameters.class);
 404  
 
 405  0
                     if (getOutputClass.getReturnType() == String.class) {
 406  0
                         outputClass = (String) getOutputClass.invoke(null, vp);
 407  0
                         outputOrder = new String[0];
 408  
 
 409  
                         try {
 410  0
                             Method getOutputOrder = clazz.getDeclaredMethod("getOutputOrder",
 411  
                                                                             TupleFlowParameters.class);
 412  0
                             outputOrder = (String[]) getOutputOrder.invoke(null, vp);
 413  0
                         } catch (NoSuchMethodException e) {
 414  
                             // ignore this one
 415  0
                         }
 416  
 
 417  0
                         if (!Verification.isClassAvailable(outputClass)) {
 418  0
                             store.addError(step.getLocation(),
 419  
                                            step.getClassName() + ": Class " + step.getClassName() + " returned " +
 420  
                                            "an output class name '" + outputClass + "' which couldn't be found.");
 421  0
                             state.setDefined(false);
 422  
                         } else {
 423  0
                             state.update(outputClass, outputOrder);
 424  
                         }
 425  
                     } else {
 426  0
                         store.addError(step.getLocation(), step.getClassName() + " has a class method called getOutputClass, " +
 427  
                                        "but it returns something other than java.lang.String.");
 428  0
                         state.setDefined(false);
 429  
                     }
 430  0
                 } catch (NoSuchMethodException e) {
 431  0
                     store.addWarning(step.getLocation(), step.getClassName() + ": Class " + step.
 432  
                                      getClassName() + " has no suitable " +
 433  
                                      "getOutputClass method and no @OutputClass annotation.");
 434  0
                     state.setDefined(false);
 435  0
                 }
 436  
             }
 437  0
         } catch (InvocationTargetException e) {
 438  0
             store.addError(step.getLocation(),
 439  
                            step.getClassName() + ": Caught an InvocationTargetException while verifying class: " + e.
 440  
                            getMessage());
 441  0
             state.setDefined(false);
 442  0
         } catch (SecurityException e) {
 443  0
             store.addError(step.getLocation(),
 444  
                            step.getClassName() + ": Caught a SecurityException while verifying class: " + e.
 445  
                            getMessage());
 446  0
             state.setDefined(false);
 447  0
         } catch (IllegalArgumentException e) {
 448  0
             store.addError(step.getLocation(),
 449  
                            step.getClassName() + ": Caught an IllegalArgumentException while verifying class: " + e.
 450  
                            getMessage());
 451  0
             state.setDefined(false);
 452  0
         } catch (IllegalAccessException e) {
 453  0
             store.addError(step.getLocation(),
 454  
                            step.getClassName() + ": Caught an IllegalAccessException while verifying class: " + e.
 455  
                            getMessage());
 456  0
             state.setDefined(false);
 457  0
         }
 458  0
     }
 459  
 
 460  
     private static void verifyStepClass(final Class clazz, final Step step, final ErrorStore store, final VerificationParameters vp) {
 461  
         try {
 462  0
             Verified verifiedAnnotation = (Verified) clazz.getAnnotation(Verified.class);
 463  
 
 464  
             // if this class is already verified, we can move on
 465  0
             if (verifiedAnnotation != null) {
 466  0
                 return;
 467  
             }
 468  0
             Method verify = clazz.getDeclaredMethod("verify", TupleFlowParameters.class,
 469  
                                                     ErrorHandler.class);
 470  
 
 471  0
             if (verify == null) {
 472  0
                 store.addWarning(step.getLocation(), "Class " + step.getClassName() +
 473  
                                  " has no suitable verify method.");
 474  0
             } else if (Modifier.isStatic(verify.getModifiers()) == false) {
 475  0
                 store.addWarning(step.getLocation(), "Class " + step.getClassName() +
 476  
                                  " has a verify method, but it isn't static.");
 477  
             } else {
 478  0
                 verify.invoke(null, vp,
 479  
                               new VerificationErrorHandler(store, step.getLocation()));
 480  
             }
 481  0
         } catch (InvocationTargetException e) {
 482  0
             store.addError(step.getLocation(),
 483  
                            step.getClassName() + ": Caught an InvocationTargetException while verifying class: " + e.
 484  
                            getMessage());
 485  0
         } catch (SecurityException e) {
 486  0
             store.addError(step.getLocation(),
 487  
                            step.getClassName() + ": Caught a SecurityException while verifying class: " + e.
 488  
                            getMessage());
 489  0
         } catch (NoSuchMethodException e) {
 490  0
             store.addWarning(step.getLocation(),
 491  
                              "Class " + step.getClassName() + " has no suitable verify method.");
 492  0
         } catch (IllegalArgumentException e) {
 493  0
             store.addError(step.getLocation(),
 494  
                            step.getClassName() + ": Caught an IllegalArgumentException while verifying class: " + e.
 495  
                            getMessage());
 496  0
         } catch (IllegalAccessException e) {
 497  0
             store.addError(step.getLocation(),
 498  
                            step.getClassName() + ": Caught an IllegalAccessException while verifying class: " + e.
 499  
                            getMessage());
 500  0
         }
 501  0
     }
 502  
     
 503  
     private static Class findInputClassType(Class clazz) {
 504  0
         Method[] allMethods = clazz.getMethods();
 505  
         
 506  0
         for (Method method : allMethods) {
 507  0
             if (!method.getName().equals("process"))
 508  0
                 continue;
 509  0
             Class[] types = method.getParameterTypes();
 510  0
             if (types.length != 1)
 511  0
                 continue;
 512  0
             if (types[0] == Object.class)
 513  0
                 continue;
 514  0
             return types[0];
 515  
         }
 516  0
         return null;
 517  
     }
 518  
 
 519  
     private static void verifyInputClass(TypeState state, final Step step, final Class clazz, final VerificationParameters vp, final ErrorStore store) {
 520  0
         if (!state.isDefined()) {
 521  0
             return;
 522  
         }
 523  
         try {
 524  0
             Class inputClass = findInputClassType(clazz);
 525  
             
 526  0
             InputClass inputClassAnnotation = (InputClass) clazz.getAnnotation(InputClass.class);
 527  0
             String inputClassName = "unknown";
 528  0
             String[] inputOrder = new String[0];
 529  
 
 530  0
             if (inputClassAnnotation != null) {
 531  0
                 inputClassName = inputClassAnnotation.className();
 532  0
                 inputOrder = inputClassAnnotation.order();
 533  
                 
 534  0
                 if (inputClass != null && !inputClassName.equals(inputClass.getName())) {
 535  0
                     String outputMessage = String.format("%s: Class %s has an @InputClass " +
 536  
                             "annotation with the class name '%s', but the process() method takes " +
 537  
                             "'%s' objects.", step.getClassName(), step.getClassName(),
 538  
                             inputClassName, inputClass.getName());
 539  0
                     store.addError(step.getLocation(), outputMessage);
 540  
                 }
 541  
 
 542  0
                 if (!Verification.isClassAvailable(inputClassName)) {
 543  0
                     store.addError(step.getLocation(), step.getClassName() + ": Class " + step.
 544  
                                    getClassName() + " has an " +
 545  
                                    "@InputClass annotation with the class name '" + inputClassName +
 546  
                                    "' which couldn't be found.");
 547  
                 }
 548  0
             } else if (inputClass != null) {
 549  0
                 inputClassName = inputClass.getName();
 550  0
             } else if (inputClass == null) {
 551  
                 try {
 552  0
                     Method getInputClass = clazz.getDeclaredMethod("getInputClass",
 553  
                                                                    TupleFlowParameters.class);
 554  
 
 555  0
                     if (getInputClass.getReturnType() == String.class) {
 556  0
                         inputClassName = (String) getInputClass.invoke(null, vp);
 557  
 
 558  
                         try {
 559  0
                             Method getInputOrder = clazz.getDeclaredMethod("getInputOrder",
 560  
                                                                            TupleFlowParameters.class);
 561  0
                             inputOrder = (String[]) getInputOrder.invoke(null, vp);
 562  0
                         } catch (NoSuchMethodException e) {
 563  
                             // ignore this one
 564  0
                         }
 565  
 
 566  0
                         if (!Verification.isClassAvailable(inputClassName)) {
 567  0
                             store.addError(step.getLocation(), step.getClassName() + ": Class " + step.getClassName() + " has an " +
 568  
                                            "returned '" + inputClassName + "' from getInputClass, but " +
 569  
                                            "it couldn't be found.");
 570  
                         }
 571  
                     } else {
 572  0
                         store.addError(step.getLocation(), step.getClassName() + " has a class method called getInputClass, " +
 573  
                                        "but it returns something other than java.lang.String.");
 574  
                     }
 575  0
                 } catch (NoSuchMethodException e) {
 576  0
                     store.addWarning(step.getLocation(), step.getClassName() + ": Class " + step.
 577  
                                      getClassName() + " has no suitable " +
 578  
                                      "getInputClass method and has no @InputClass annotation.");
 579  0
                     return;
 580  0
                 }
 581  
             }
 582  
 
 583  0
             if (state.isDefined()) {
 584  0
                 if (!inputClassName.equals(state.getClassName())) {
 585  0
                     store.addError(step.getLocation(), "Current pipeline class '" + state.
 586  
                                    getClassName() +
 587  
                                    "' is different than the required type: '" +
 588  
                                    inputClassName + "'.");
 589  
                 }
 590  
 
 591  0
                 if (!compatibleOrders(state.getOrder(), inputOrder)) {
 592  0
                     store.addError(step.getLocation(),
 593  
                                    "Current object order '" + Arrays.toString(state.getOrder()) + "' is incompatible " +
 594  
                                    "with the required input order: '" + Arrays.toString(inputOrder) + "'.");
 595  
                 }
 596  
             }
 597  0
         } catch (InvocationTargetException e) {
 598  0
             store.addError(step.getLocation(),
 599  
                            step.getClassName() + ": Caught an InvocationTargetException while verifying class: " + e.
 600  
                            getMessage());
 601  0
         } catch (SecurityException e) {
 602  0
             store.addError(step.getLocation(),
 603  
                            step.getClassName() + ": Caught a SecurityException while verifying class: " + e.
 604  
                            getMessage());
 605  0
         } catch (IllegalArgumentException e) {
 606  0
             store.addError(step.getLocation(),
 607  
                            step.getClassName() + ": Caught an IllegalArgumentException while verifying class: " + e.
 608  
                            getMessage());
 609  0
         } catch (IllegalAccessException e) {
 610  0
             store.addError(step.getLocation(),
 611  
                            step.getClassName() + ": Caught an IllegalAccessException while verifying class: " + e.
 612  
                            getMessage());
 613  0
         }
 614  0
     }
 615  
 
 616  
     public static void verify(Stage stage, ErrorStore store) {
 617  0
         TypeState state = new TypeState();
 618  0
         verify(state, stage, stage.steps, store);
 619  0
     }
 620  
 
 621  
     public static void verify(Job job, ErrorStore store) {
 622  0
         for (Stage stage : job.stages.values()) {
 623  0
             verify(stage, store);
 624  
         }
 625  0
     }
 626  
 
 627  
     public static boolean verifyTypeReader(String readerName, Class typeClass, TupleFlowParameters parameters, ErrorHandler handler) {
 628  0
         return verifyTypeReader(readerName, typeClass, new String[0], parameters, handler);
 629  
     }
 630  
 
 631  
     public static boolean verifyTypeReader(String readerName, Class typeClass, String[] order, TupleFlowParameters parameters, ErrorHandler handler) {
 632  0
         if (!parameters.readerExists(readerName, typeClass.getName(), order)) {
 633  0
             handler.addError("No reader named '" + readerName + "' was found in this stage.");
 634  0
             return false;
 635  
         }
 636  
 
 637  0
         return true;
 638  
     }
 639  
 
 640  
     public static boolean verifyTypeWriter(String readerName, Class typeClass, String order[], TupleFlowParameters parameters, ErrorHandler handler) {
 641  0
         if (!parameters.writerExists(readerName, typeClass.getName(), order)) {
 642  0
             handler.addError("No writer named '" + readerName + "' was found in this stage.");
 643  0
             return false;
 644  
         }
 645  
 
 646  0
         return true;
 647  
     }
 648  
 }