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