015e0c4b0ae3f760d769bf52380238bc9e28d0fe
[mdsal.git] / binding / mdsal-binding-test-utils / src / main / java / org / opendaylight / mdsal / binding / testutils / XtendYangBeanGenerator.java
1 /*
2  * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.testutils;
9
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;
20
21 /**
22  * {@link XtendBeanGenerator} customized for YANG beans stored in MD SAL
23  * DataBroker.
24  *
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;
29  *
30  * <p>(b) to integrate it with the {@link XtendBuilderExtensions}
31  * (for ">>" instead of "->" and no build() method calls);
32  *
33  * <p>(c) to integrate it with the {@link AugmentableExtension}.
34  *
35  * @see XtendBeanGenerator
36  *
37  * @author Michael Vorburger
38  */
39 // package-local: no need to expose this, consider it an implementation detail; public API is the AssertDataObjects
40 class XtendYangBeanGenerator extends XtendBeanGenerator {
41
42     @Override
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();
48         }
49         return beanText;
50     }
51
52     @Override
53     protected boolean isUsingBuilder(final Object bean, final Class<?> builderClass) {
54         return !useBuilderExtensions(bean) && super.isUsingBuilder(bean, builderClass);
55     }
56
57     @Override
58     protected String getOperator(final Object bean, final Class<?> builderClass) {
59         return useBuilderExtensions(bean) ? ">>" : super.getOperator(bean, builderClass);
60     }
61
62     @Override
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());
70             }
71             return super.getNewBeanExpression(dataContainerBean, optBuilderClass.get());
72         }
73         return super.getNewBeanExpression(bean);
74     }
75
76     @Override
77     protected String stringify(final Class<?> klass) {
78         return klass.getSimpleName();
79     }
80
81     @Override
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"));
85     }
86
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);
92             }
93         }
94         return Optional.empty();
95     }
96
97     private static ClassToInstanceMap<Augmentation<?>> augmentations(final Augmentable<?> augmentable) {
98         return ImmutableClassToInstanceMap.copyOf(augmentable.augmentations());
99     }
100
101     @Override
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(')');
112                 });
113             return sb;
114         }
115         return "";
116     }
117
118 /*
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.
123
124     @Override
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);
130         } else {
131             return Collections.emptyList();
132         }
133     }
134  */
135
136     private static boolean useBuilderExtensions(final Object bean) {
137         return bean instanceof DataObject;
138     }
139 }