@Nullable @Override public ModelSchemaAspectExtractionResult extract(ModelSchemaExtractionContext<?> extractionContext, final List<ModelPropertyExtractionResult<?>> propertyResults) { ImmutableSet.Builder<ModelProperty<?>> dimensionsBuilder = ImmutableSet.builder(); for (ModelPropertyExtractionResult<?> propertyResult : propertyResults) { ModelProperty<?> property = propertyResult.getProperty(); for (PropertyAccessorExtractionContext accessor : propertyResult.getAccessors()) { if (accessor.isAnnotationPresent(Variant.class)) { if (accessor.getAccessorType() == PropertyAccessorType.SETTER) { throw invalidProperty(extractionContext, property, "@Variant annotation is only allowed on getter methods"); } Class<?> propertyType = property.getType().getRawClass(); if (!String.class.equals(propertyType) && !Named.class.isAssignableFrom(propertyType)) { throw invalidProperty(extractionContext, property, String.format("@Variant annotation only allowed for properties of type String and %s, but property has type %s", Named.class.getName(), propertyType.getName())); } dimensionsBuilder.add(property); } } } ImmutableSet<ModelProperty<?>> dimensions = dimensionsBuilder.build(); if (dimensions.isEmpty()) { return null; } return new ModelSchemaAspectExtractionResult(new VariantAspect(dimensions)); }
protected <T extends Named> Set<T> chooseElements(Class<T> type, Set<? extends T> candidates, Set<String> names) { if (names.isEmpty()) { return new LinkedHashSet<T>(candidates); } Set<String> unusedNames = new HashSet<String>(names); Set<T> chosen = new LinkedHashSet<T>(); for (T candidate : candidates) { if (unusedNames.remove(candidate.getName())) { chosen.add(candidate); } } if (!unusedNames.isEmpty()) { throw new InvalidUserDataException(String.format("Invalid %s: '%s'", type.getSimpleName(), unusedNames.iterator().next())); } return chosen; }
/** * Converts a value into a string. Never returns {@code null}. * * <p>Rules used to determine the string value:</p> * * <ul> * <li>the {@code null} value is converted to the string {@code "null"},</li> * <li>if the value has a type that overrides {@link Object#toString()} then it is converted to {@code value.toString()},</li> * <li>if the value's type implements {@link Named} then it is converted to {@code value.getName()},</li> * <li>otherwise the return value of {@link Object#toString()} is used.</li> * </ul> * * <p>The method returns the converted value, unless it is {@code null}, in which case the string {@code "null"} is returned instead.</p> */ public static String displayValueOf(Object value) { String result = null; if (value != null) { boolean hasCustomToString; try { Method toString = value.getClass().getMethod("toString"); hasCustomToString = !toString.getDeclaringClass().equals(Object.class); } catch (NoSuchMethodException ignore) { hasCustomToString = false; } if (!hasCustomToString && Named.class.isAssignableFrom(value.getClass())) { result = ((Named) value).getName(); } else { result = value.toString(); } } if (result == null) { result = "null"; } return result; }
public String determineName(Object thing) { Object name; try { if (thing instanceof Named) { name = ((Named) thing).getName(); } else if (thing instanceof Map) { name = ((Map) thing).get("name"); } else if (thing instanceof GroovyObject) { name = ((GroovyObject) thing).getProperty("name"); } else { name = DynamicObjectUtil.asDynamicObject(thing).getProperty("name"); } } catch (MissingPropertyException e) { throw new NoNamingPropertyException(thing); } if (name == null) { throw new NullNamingPropertyException(thing); } return name.toString(); }
private <T extends Named> Set<T> chooseElements(Class<T> type, Set<? extends T> candidates, final Set<String> names) { if (names.isEmpty()) { return new LinkedHashSet<T>(candidates); } Set<String> unusedNames = new HashSet<String>(names); Set<T> chosen = new LinkedHashSet<T>(); for (T candidate : candidates) { if (unusedNames.remove(candidate.getName())) { chosen.add(candidate); } } if (!unusedNames.isEmpty()) { throw new InvalidUserDataException(String.format("Invalid %s: '%s'", type.getSimpleName(), unusedNames.iterator().next())); } return chosen; }
@Override public String getValueAsString(String variantAxis) { Object o = variantCoordinates.get(variantAxis); if (o instanceof Named) { return ((Named) o).getName(); } if (o instanceof String) { return (String) o; } return o == null ? null : o.toString(); }
@Override public boolean isCompatibleWithRequirement(Object requirement, Object value) { if (requirement instanceof String) { return requirement.equals(value); } else if (requirement instanceof Named) { return ((Named) requirement).getName().equals(((Named) value).getName()); } return false; }
@Override public <T extends Named> BinaryNamingScheme withVariantDimension(T value, Collection<? extends T> allValuesForAxis) { if (allValuesForAxis.size() == 1) { return this; } return withVariantDimension(value.getName()); }
private static void validateManagedProperty(StructBindingExtractionContext<?> extractionContext, String propertyName, ModelSchema<?> propertySchema, boolean writable, boolean isDeclaredAsHavingUnmanagedType) { if (propertyName.equals("name") && Named.class.isAssignableFrom(extractionContext.getPublicSchema().getType().getRawClass())) { if (writable) { extractionContext.add(propertyName, String.format("it must not have a setter, because the type implements '%s'", Named.class.getName())); } return; } // Only managed implementation and value types are allowed as a managed property type unless marked with @Unmanaged boolean isAllowedPropertyTypeOfManagedType = propertySchema instanceof ManagedImplSchema || propertySchema instanceof ScalarValueSchema; ModelType<?> propertyType = propertySchema.getType(); if (isAllowedPropertyTypeOfManagedType && isDeclaredAsHavingUnmanagedType) { extractionContext.add(propertyName, String.format("it is marked as @Unmanaged, but is of @Managed type '%s'; please remove the @Managed annotation", propertyType.getDisplayName() )); } if (!writable && isDeclaredAsHavingUnmanagedType) { extractionContext.add(propertyName, "it must not be read only, because it is marked as @Unmanaged"); } if (!(extractionContext.getPublicSchema() instanceof RuleSourceSchema)) { if (propertySchema instanceof CollectionSchema) { if (!(propertySchema instanceof ScalarCollectionSchema) && writable) { extractionContext.add(propertyName, String.format("it cannot have a setter (%s properties must be read only)", propertyType.getRawClass().getSimpleName())); } } } }
@Override @SuppressWarnings("unchecked") public <T extends Named> T getValueAsType(Class<T> clazz, String variantAxis) { return (T) variantCoordinates.get(variantAxis); }
private boolean isNamedType() { return Named.class.isAssignableFrom(bindings.getPublicSchema().getType().getRawClass()); }
public DefaultNamedDomainObjectSet(Class<? extends T> type, Instantiator instantiator) { this(type, instantiator, Named.Namer.forType(type)); }
protected AbstractNamedDomainObjectContainer(Class<T> type, Instantiator instantiator) { super(type, instantiator, Named.Namer.forType(type)); }
/** * Wraps the given items in a named domain object set. */ public static <T extends Named> NamedDomainObjectSet<T> toNamedDomainObjectSet(Class<T> type, T... items) { DefaultNamedDomainObjectSet<T> domainObjectSet = new DefaultNamedDomainObjectSet<T>(type, DirectInstantiator.INSTANCE); CollectionUtils.addAll(domainObjectSet, items); return domainObjectSet; }
private <T extends Named> BinaryNamingSchemeBuilder maybeAddDimension(BinaryNamingSchemeBuilder builder, T variation, Collection<T> variations) { if (variations.size() > 1) { builder = builder.withVariantDimension(variation.getName()); } return builder; }
/** * Wraps the given items in a named domain object set. */ public static <T extends Named> NamedDomainObjectSet<T> toNamedDomainObjectSet(Class<T> type, T... items) { DefaultNamedDomainObjectSet<T> domainObjectSet = new DefaultNamedDomainObjectSet<T>(type, new DirectInstantiator()); CollectionUtils.addAll(domainObjectSet, items); return domainObjectSet; }
/** * Creates a copy of this scheme, <em>adding</em> a variant dimension if required. */ <T extends Named> BinaryNamingScheme withVariantDimension(T value, Collection<? extends T> allValuesForAxis);
/** * Returns a transformer that names {@link Named} objects. * * Nulls are returned as null. * * @return The naming transformer. */ public static Transformer<String, Named> name() { return name(Named.Namer.INSTANCE); }
/** * <p>Creates a container that instantiates reflectively, expecting a 1 arg constructor taking the name.<p> * * <p>The type must implement the {@link Named} interface as a {@link Namer} will be created based on this type.</p> * * @param type The concrete type of element in the container (must implement {@link Named}) * @param instantiator The instantiator to use to create any other collections based on this one */ public FactoryNamedDomainObjectContainer(Class<T> type, Instantiator instantiator) { this(type, instantiator, Named.Namer.forType(type)); }
/** * <p>Creates a container that instantiates using the given factory.<p> * * @param type The concrete type of element in the container (must implement {@link Named}) * @param instantiator The instantiator to use to create any other collections based on this one * @param factory The factory responsible for creating new instances on demand */ public FactoryNamedDomainObjectContainer(Class<T> type, Instantiator instantiator, NamedDomainObjectFactory<T> factory) { this(type, instantiator, Named.Namer.forType(type), factory); }
/** * <p>Creates a container that instantiates using the given factory.<p> * * @param type The concrete type of element in the container (must implement {@link Named}) * @param instantiator The instantiator to use to create any other collections based on this one * @param factoryClosure The closure responsible for creating new instances on demand */ public FactoryNamedDomainObjectContainer(Class<T> type, Instantiator instantiator, final Closure factoryClosure) { this(type, instantiator, Named.Namer.forType(type), factoryClosure); }
/** * Returns a transformer that names {@link Named} objects. * * Nulls are returned as null. * * @return The naming transformer. */ public static Transformer<String, Named> name() { return name(new Named.Namer()); }
<T extends Named> T getValueAsType(Class<T> clazz, String variantAxis);