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.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;
21 * {@link XtendBeanGenerator} customized for YANG beans stored in MD SAL
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;
29 * <p>(b) to integrate it with the {@link XtendBuilderExtensions}
30 * (for ">>" instead of "->" and no build() method calls);
32 * <p>(c) to integrate it with the {@link AugmentableExtension}.
34 * @see XtendBeanGenerator
36 * @author Michael Vorburger
38 // package-local: no need to expose this, consider it an implementation detail; public API is the AssertDataObjects
39 class XtendYangBeanGenerator extends XtendBeanGenerator {
41 private final AugmentableExtension augmentableExtension = new AugmentableExtension();
44 public String getExpression(final Object bean) {
45 final String beanText = super.getExpression(bean);
46 if (useBuilderExtensions(bean)) {
47 return new StringBuilder("import static extension ").append(XtendBuilderExtensions.class.getName())
48 .append(".operator_doubleGreaterThan\n\n").append(beanText).toString();
54 protected boolean isUsingBuilder(final Object bean, final Class<?> builderClass) {
55 return useBuilderExtensions(bean) ? false : super.isUsingBuilder(bean, builderClass);
59 protected String getOperator(final Object bean, final Class<?> builderClass) {
60 return useBuilderExtensions(bean) ? ">>" : super.getOperator(bean, builderClass);
64 protected CharSequence getNewBeanExpression(final Object bean) {
65 if (bean instanceof DataContainer) {
66 DataContainer dataContainerBean = (DataContainer) bean;
67 Optional<Class<?>> optBuilderClass = getOptionalBuilderClassByAppendingBuilderToClassName(
68 dataContainerBean.implementedInterface());
69 if (optBuilderClass.isEmpty()) {
70 throw new IllegalArgumentException("DataContainer has no *Builder class: " + bean.getClass());
72 return super.getNewBeanExpression(dataContainerBean, optBuilderClass.get());
74 return super.getNewBeanExpression(bean);
78 protected String stringify(final Class<?> klass) {
79 return klass.getSimpleName();
83 protected Iterable<Property> filter(final Iterable<Property> properties) {
84 // YANG keys duplicate existing other properties (there are getter/setter for both), so filter them
85 return Iterables.filter(properties, property -> !property.getName().equals("key"));
88 private Optional<ClassToInstanceMap<Augmentation<?>>> getAugmentations(final Object bean) {
89 if (bean instanceof Augmentable<?>) {
90 Augmentable<?> augmentable = (Augmentable<?>) bean;
91 ClassToInstanceMap<Augmentation<?>> augmentables = augmentableExtension.getAugmentations(augmentable);
92 if (!augmentables.isEmpty()) {
93 return Optional.of(augmentables);
96 return Optional.empty();
100 protected CharSequence getAdditionalInitializationExpression(final Object bean, final Class<?> builderClass) {
101 Optional<ClassToInstanceMap<Augmentation<?>>> optional = getAugmentations(bean);
102 if (optional.isPresent()) {
103 StringBuilder sb = new StringBuilder();
104 optional.get().entrySet().stream()
105 // We sort the augmentations by Class type, because the Map has unpredictable order:
106 .sorted(Comparator.comparing(e2 -> e2.getKey().getName()))
107 .forEachOrdered(e -> {
108 sb.append("addAugmentation(").append(stringify(e.getKey())).append(", ")
109 .append(getNewBeanExpression(e.getValue())).append(')');
117 TODO activate this once YANG objects either have a setAugmentations(Map)
118 or implement a new TBD interface AugmentableBuilder with a method like:
119 <E extends Augmentation<T>> Builder? addAugmentation(Class<E> augmentationType, E augmentation);
120 which an extension method could jump on.
123 public Iterable<Property> getAdditionalSpecialProperties(Object bean, Class<?> builderClass) {
124 Optional<ClassToInstanceMap<Augmentation<?>>> optional = getAugmentations(bean);
125 if (optional.isPresent()) {
126 Property augmentableProperty = new Property("augmentations", true, Map.class, () -> optional.get(), null);
127 return Collections.singleton(augmentableProperty);
129 return Collections.emptyList();
134 private static boolean useBuilderExtensions(final Object bean) {
135 return bean instanceof DataObject;