2 * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.testutils;
10 import ch.vorburger.xtendbeans.XtendBeanGenerator;
11 import com.google.common.collect.ClassToInstanceMap;
12 import com.google.common.collect.ImmutableClassToInstanceMap;
13 import com.google.common.collect.Iterables;
14 import java.util.Comparator;
15 import java.util.Optional;
16 import org.opendaylight.yangtools.yang.binding.Augmentable;
17 import org.opendaylight.yangtools.yang.binding.Augmentation;
18 import org.opendaylight.yangtools.yang.binding.DataContainer;
19 import org.opendaylight.yangtools.yang.binding.DataObject;
22 * {@link XtendBeanGenerator} customized for YANG beans stored in MD SAL
25 * <p>This is required: (a) because YANG model DataObject beans (when read from a
26 * DataBroker) are funky java.lang.reflect.Proxy instances, and XtendBeanGenerator
27 * cannot find the Builder or the property getters for them without a bit of help,
28 * which this class provides;
30 * <p>(b) to integrate it with the {@link XtendBuilderExtensions}
31 * (for ">>" instead of "->" and no build() method calls);
33 * <p>(c) to integrate it with the {@link AugmentableExtension}.
35 * @see XtendBeanGenerator
37 * @author Michael Vorburger
39 // package-local: no need to expose this, consider it an implementation detail; public API is the AssertDataObjects
40 class XtendYangBeanGenerator extends XtendBeanGenerator {
43 public String getExpression(final Object bean) {
44 final String beanText = super.getExpression(bean);
45 if (useBuilderExtensions(bean)) {
46 return new StringBuilder("import static extension ").append(XtendBuilderExtensions.class.getName())
47 .append(".operator_doubleGreaterThan\n\n").append(beanText).toString();
53 protected boolean isUsingBuilder(final Object bean, final Class<?> builderClass) {
54 return !useBuilderExtensions(bean) && super.isUsingBuilder(bean, builderClass);
58 protected String getOperator(final Object bean, final Class<?> builderClass) {
59 return useBuilderExtensions(bean) ? ">>" : super.getOperator(bean, builderClass);
63 protected CharSequence getNewBeanExpression(final Object bean) {
64 if (bean instanceof DataContainer) {
65 DataContainer dataContainerBean = (DataContainer) bean;
66 Optional<Class<?>> optBuilderClass = getOptionalBuilderClassByAppendingBuilderToClassName(
67 dataContainerBean.implementedInterface());
68 if (optBuilderClass.isEmpty()) {
69 throw new IllegalArgumentException("DataContainer has no *Builder class: " + bean.getClass());
71 return super.getNewBeanExpression(dataContainerBean, optBuilderClass.get());
73 return super.getNewBeanExpression(bean);
77 protected String stringify(final Class<?> klass) {
78 return klass.getSimpleName();
82 protected Iterable<Property> filter(final Iterable<Property> properties) {
83 // YANG keys duplicate existing other properties (there are getter/setter for both), so filter them
84 return Iterables.filter(properties, property -> !property.getName().equals("key"));
87 private static Optional<ClassToInstanceMap<Augmentation<?>>> getAugmentations(final Object bean) {
88 if (bean instanceof Augmentable<?>) {
89 ClassToInstanceMap<Augmentation<?>> augmentables = augmentations((Augmentable<?>) bean);
90 if (!augmentables.isEmpty()) {
91 return Optional.of(augmentables);
94 return Optional.empty();
97 private static ClassToInstanceMap<Augmentation<?>> augmentations(final Augmentable<?> augmentable) {
98 return ImmutableClassToInstanceMap.copyOf(augmentable.augmentations());
102 protected CharSequence getAdditionalInitializationExpression(final Object bean, final Class<?> builderClass) {
103 Optional<ClassToInstanceMap<Augmentation<?>>> optional = getAugmentations(bean);
104 if (optional.isPresent()) {
105 StringBuilder sb = new StringBuilder();
106 optional.get().entrySet().stream()
107 // We sort the augmentations by Class type, because the Map has unpredictable order:
108 .sorted(Comparator.comparing(e2 -> e2.getKey().getName()))
109 .forEachOrdered(e -> {
110 sb.append("addAugmentation(").append(stringify(e.getKey())).append(", ")
111 .append(getNewBeanExpression(e.getValue())).append(')');
119 TODO activate this once YANG objects either have a setAugmentations(Map)
120 or implement a new TBD interface AugmentableBuilder with a method like:
121 <E extends Augmentation<T>> Builder? addAugmentation(Class<E> augmentationType, E augmentation);
122 which an extension method could jump on.
125 public Iterable<Property> getAdditionalSpecialProperties(Object bean, Class<?> builderClass) {
126 Optional<ClassToInstanceMap<Augmentation<?>>> optional = getAugmentations(bean);
127 if (optional.isPresent()) {
128 Property augmentableProperty = new Property("augmentations", true, Map.class, () -> optional.get(), null);
129 return Collections.singleton(augmentableProperty);
131 return Collections.emptyList();
136 private static boolean useBuilderExtensions(final Object bean) {
137 return bean instanceof DataObject;