fb12ea3e7da50d7ebaa59fcd3180dd13572429f0
[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             Optional<Class<?>> optBuilderClass = getOptionalBuilderClassByAppendingBuilderToClassName(
80                     dataContainerBean.getImplementedInterface());
81             if (optBuilderClass.isPresent()) {
82                 return super.getNewBeanExpression(dataContainerBean, optBuilderClass.get());
83             } else {
84                 throw new IllegalArgumentException("DataContainer has no *Builder class: " + bean.getClass());
85             }
86         } else {
87             return super.getNewBeanExpression(bean);
88         }
89     }
90
91     @Override
92     protected String stringify(Class<?> klass) {
93         return klass.getSimpleName();
94     }
95
96     @Override
97     protected Iterable<Property> filter(Iterable<Property> properties) {
98         // YANG keys duplicate existing other properties (there are getter/setter for both), so filter them
99         return Iterables.filter(properties, property -> !property.getName().equals("key"));
100     }
101
102     private Optional<ClassToInstanceMap<Augmentation<?>>> getAugmentations(Object bean) {
103         if (bean instanceof Augmentable<?>) {
104             Augmentable<?> augmentable = (Augmentable<?>) bean;
105             ClassToInstanceMap<Augmentation<?>> augmentables = augmentableExtension.getAugmentations(augmentable);
106             if (!augmentables.isEmpty()) {
107                 return Optional.of(augmentables);
108             }
109         }
110         return Optional.empty();
111     }
112
113     @Override
114     protected CharSequence getAdditionalInitializationExpression(Object bean, Class<?> builderClass) {
115         Optional<ClassToInstanceMap<Augmentation<?>>> optional = getAugmentations(bean);
116         if (optional.isPresent()) {
117             StringBuilder sb = new StringBuilder();
118             optional.get().forEach((klass, augmentation) -> {
119                 sb.append("addAugmentation(");
120                 sb.append(stringify(klass));
121                 sb.append(", ");
122                 sb.append(getNewBeanExpression(augmentation));
123                 sb.append(")");
124             });
125             return sb;
126         } else {
127             return "";
128         }
129     }
130
131 /*
132     TODO activate this once YANG objects either have a setAugmentations(Map)
133       or implement a new TBD interface AugmentableBuilder with a method like:
134           <E extends Augmentation<T>> Builder? addAugmentation(Class<E> augmentationType, E augmentation);
135       which an extension method could jump on.
136
137     @Override
138     public Iterable<Property> getAdditionalSpecialProperties(Object bean, Class<?> builderClass) {
139         Optional<ClassToInstanceMap<Augmentation<?>>> optional = getAugmentations(bean);
140         if (optional.isPresent()) {
141             Property augmentableProperty = new Property("augmentations", true, Map.class, () -> optional.get(), null);
142             return Collections.singleton(augmentableProperty);
143         } else {
144             return Collections.emptyList();
145         }
146     }
147  */
148
149 }