class DynamicSpec {

  public static DynamicSpec[] parseRules(final String rule) {
    final String[] rules = rule.trim().split(";");
    final DynamicSpec[] specs = new DynamicSpec[rules.length];
    for (int i = 0; i < rules.length; ++i) {
      final String[] classNameMethodSpec = rules[i].split("#");
      if (classNameMethodSpec.length != 2)
        throw error(rules[i]);

      final boolean polymorphic = classNameMethodSpec[0].startsWith("^");
      if (polymorphic)
        classNameMethodSpec[0] = classNameMethodSpec[0].substring(1).trim();

      trim(classNameMethodSpec);
      final String className = classNameMethodSpec[0];
      if (className.isEmpty())
        throw error(rules[i]);

      final String returning;
      final int col = classNameMethodSpec[1].indexOf(':');
      if (col == -1) {
        returning = null;
      }
      else {
        if (classNameMethodSpec[1].endsWith(":"))
          throw error(rules[i]);

        returning = classNameMethodSpec[1].substring(col + 1).trim();
        if (returning.isEmpty() || returning.contains(":"))
          throw error(rules[i]);

        classNameMethodSpec[1] = classNameMethodSpec[1].substring(0, col).trim();
      }

      final String methodName;
      final String[] args;
      final int start = classNameMethodSpec[1].indexOf('(');
      if (start == -1) {
        methodName = classNameMethodSpec[1];
        args = null;
      }
      else {
        final int end = classNameMethodSpec[1].indexOf(')', start + 1);
        if (end == -1)
          throw error(rules[i]);

        args = start + 1 == end ? empty : classNameMethodSpec[1].substring(start + 1, end).split(",");
        trim(args);

        methodName = classNameMethodSpec[1].substring(0, start).trim();
      }

      if (methodName.isEmpty() || ("<init>".equals(methodName) && returning != null) || ("<clinit>".equals(methodName) && (args != null || returning != null)))
        throw error(rules[i]);

      specs[i] = new DynamicSpec(polymorphic, className, methodName, args, returning);
    }

    return specs;
  }

  private  DynamicSpec(final boolean polymorphic, final String className, final String methodName, final String[] args, final String returning);

}