public static VariantsMetaData extractFrom(BinarySpec spec) {
    Map<String, Object> variants = Maps.newHashMap();
    Class<? extends BinarySpec> specClass = spec.getClass();
    Set<Class<?>> interfaces = ClassInspector.inspect(specClass).getSuperTypes();
    for (Class<?> intf : interfaces) {
      ClassDetails details = ClassInspector.inspect(intf);
      Collection<? extends PropertyDetails> properties = details.getProperties();
      for (PropertyDetails property : properties) {
        List<Method> getters = property.getGetters();
        for (Method getter : getters) {
          if (getter.getAnnotation(Variant.class) != null) {
            extractVariant(variants, spec, property.getName(), getter);
          }
        }
      }
    }

    return new DefaultVariantsMetaData(Collections.unmodifiableMap(variants));
  }
 private void inspectType(Class<?> type, ClassMetaData classMetaData) {
   ClassDetails classDetails = ClassInspector.inspect(type);
   for (Method method : classDetails.getAllMethods()) {
     if (method.getAnnotation(Inject.class) != null) {
       if (!Modifier.isPublic(method.getModifiers())
           && !Modifier.isProtected(method.getModifiers())) {
         throw new UnsupportedOperationException(
             String.format(
                 "Cannot attach @Inject to method %s.%s() as it is not public or protected.",
                 method.getDeclaringClass().getSimpleName(), method.getName()));
       }
       if (Modifier.isStatic(method.getModifiers())) {
         throw new UnsupportedOperationException(
             String.format(
                 "Cannot attach @Inject to method %s.%s() as it is static.",
                 method.getDeclaringClass().getSimpleName(), method.getName()));
       }
     }
   }
   for (PropertyDetails property : classDetails.getProperties()) {
     PropertyMetaData propertyMetaData = classMetaData.property(property.getName());
     for (Method method : property.getGetters()) {
       propertyMetaData.addGetter(method);
     }
     for (Method method : property.getSetters()) {
       propertyMetaData.addSetter(method);
     }
   }
   for (Method method : classDetails.getInstanceMethods()) {
     Class<?>[] parameterTypes = method.getParameterTypes();
     if (parameterTypes.length == 1) {
       classMetaData.addCandidateSetMethod(method);
     }
     if (parameterTypes.length > 0
         && parameterTypes[parameterTypes.length - 1].equals(Action.class)) {
       classMetaData.addActionMethod(method);
     } else if (parameterTypes.length > 0
         && parameterTypes[parameterTypes.length - 1].equals(Closure.class)) {
       classMetaData.addClosureMethod(method);
     }
   }
 }