53d4371f6b63590a78933af3d64dbd7cdd1ffdf2
[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.Iterables;
13 import java.util.Comparator;
14 import java.util.Optional;
15 import org.opendaylight.yangtools.yang.binding.Augmentable;
16 import org.opendaylight.yangtools.yang.binding.Augmentation;
17 import org.opendaylight.yangtools.yang.binding.DataContainer;
18 import org.opendaylight.yangtools.yang.binding.DataObject;
19
20 /**
21  * {@link XtendBeanGenerator} customized for YANG beans stored in MD SAL
22  * DataBroker.
23  *
24  * <p>This is required: (a) because YANG model DataObject beans (when read from a
25  * DataBroker) are funky java.lang.reflect.Proxy instances, and XtendBeanGenerator
26  * cannot find the Builder or the property getters for them without a bit of help,
27  * which this class provides;
28  *
29  * <p>(b) to integrate it with the {@link XtendBuilderExtensions}
30  * (for ">>" instead of "->" and no build() method calls);
31  *
32  * <p>(c) to integrate it with the {@link AugmentableExtension}.
33  *
34  * @see XtendBeanGenerator
35  *
36  * @author Michael Vorburger
37  */
38 // package-local: no need to expose this, consider it an implementation detail; public API is the AssertDataObjects
39 class XtendYangBeanGenerator extends XtendBeanGenerator {
40
41     private final AugmentableExtension augmentableExtension = new AugmentableExtension();
42
43     private boolean useBuilderExtensions(final Object bean) {
44         return bean instanceof DataObject;
45     }
46
47     @Override
48     public String getExpression(final Object bean) {
49         final String beanText = super.getExpression(bean);
50         if (useBuilderExtensions(bean)) {
51             return new StringBuilder("import static extension ").append(XtendBuilderExtensions.class.getName())
52                     .append(".operator_doubleGreaterThan\n\n").append(beanText).toString();
53         } else {
54             return beanText;
55         }
56     }
57
58     @Override
59     protected boolean isUsingBuilder(final Object bean, final Class<?> builderClass) {
60         if (useBuilderExtensions(bean)) {
61             return false;
62         } else {
63             return super.isUsingBuilder(bean, builderClass);
64         }
65     }
66
67     @Override
68     protected String getOperator(final Object bean, final Class<?> builderClass) {
69         if (useBuilderExtensions(bean)) {
70             return ">>";
71         } else {
72             return super.getOperator(bean, builderClass);
73         }
74     }
75
76     @Override
77     protected CharSequence getNewBeanExpression(final Object bean) {
78         if (bean instanceof DataContainer) {
79             DataContainer dataContainerBean = (DataContainer) bean;
80             Optional<Class<?>> optBuilderClass = getOptionalBuilderClassByAppendingBuilderToClassName(
81                     dataContainerBean.implementedInterface());
82             if (optBuilderClass.isPresent()) {
83                 return super.getNewBeanExpression(dataContainerBean, optBuilderClass.get());
84             } else {
85                 throw new IllegalArgumentException("DataContainer has no *Builder class: " + bean.getClass());
86             }
87         } else {
88             return super.getNewBeanExpression(bean);
89         }
90     }
91
92     @Override
93     protected String stringify(final Class<?> klass) {
94         return klass.getSimpleName();
95     }
96
97     @Override
98     protected Iterable<Property> filter(final Iterable<Property> properties) {
99         // YANG keys duplicate existing other properties (there are getter/setter for both), so filter them
100         return Iterables.filter(properties, property -> !property.getName().equals("key"));
101     }
102
103     private Optional<ClassToInstanceMap<Augmentation<?>>> getAugmentations(final Object bean) {
104         if (bean instanceof Augmentable<?>) {
105             Augmentable<?> augmentable = (Augmentable<?>) bean;
106             ClassToInstanceMap<Augmentation<?>> augmentables = augmentableExtension.getAugmentations(augmentable);
107             if (!augmentables.isEmpty()) {
108                 return Optional.of(augmentables);
109             }
110         }
111         return Optional.empty();
112     }
113
114     @Override
115     protected CharSequence getAdditionalInitializationExpression(final Object bean, final Class<?> builderClass) {
116         Optional<ClassToInstanceMap<Augmentation<?>>> optional = getAugmentations(bean);
117         if (optional.isPresent()) {
118             StringBuilder sb = new StringBuilder();
119             optional.get().entrySet().stream()
120                 // We sort the augmentations by Class type, because the Map has unpredictable order:
121                 .sorted(Comparator.comparing(e2 -> e2.getKey().getName()))
122                 .forEachOrdered(e -> {
123                     sb.append("addAugmentation(");
124                     sb.append(stringify(e.getKey()));
125                     sb.append(", ");
126                     sb.append(getNewBeanExpression(e.getValue()));
127                     sb.append(")");
128                 });
129             return sb;
130         } else {
131             return "";
132         }
133     }
134
135 /*
136     TODO activate this once YANG objects either have a setAugmentations(Map)
137       or implement a new TBD interface AugmentableBuilder with a method like:
138           <E extends Augmentation<T>> Builder? addAugmentation(Class<E> augmentationType, E augmentation);
139       which an extension method could jump on.
140
141     @Override
142     public Iterable<Property> getAdditionalSpecialProperties(Object bean, Class<?> builderClass) {
143         Optional<ClassToInstanceMap<Augmentation<?>>> optional = getAugmentations(bean);
144         if (optional.isPresent()) {
145             Property augmentableProperty = new Property("augmentations", true, Map.class, () -> optional.get(), null);
146             return Collections.singleton(augmentableProperty);
147         } else {
148             return Collections.emptyList();
149         }
150     }
151  */
152
153 }