Merge branch 'master' of ../controller
[yangtools.git] / yang / yang-common / src / main / java / org / opendaylight / yangtools / yang / common / AbstractCanonicalValueImplementationValidator.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.yangtools.yang.common;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
12 import java.lang.reflect.Constructor;
13 import java.lang.reflect.Modifier;
14 import java.util.Arrays;
15 import org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 @NonNullByDefault
21 abstract class AbstractCanonicalValueImplementationValidator extends ClassValue<Boolean> {
22     private static final Logger LOG = LoggerFactory.getLogger(AbstractCanonicalValueImplementationValidator.class);
23
24     @Override
25     protected final Boolean computeValue(final @Nullable Class<?> type) {
26         // Every DerivedString representation class must:
27         checkArgument(CanonicalValue.class.isAssignableFrom(type), "%s is not a DerivedString", type);
28
29         // be non-final and public
30         final int modifiers = type.getModifiers();
31         checkArgument(Modifier.isPublic(modifiers), "%s must be public", type);
32         checkArgument(!Modifier.isFinal(modifiers), "%s must not be final", type);
33
34         // have at least one public or protected constructor (for subclasses)
35         checkArgument(Arrays.stream(type.getDeclaredConstructors()).mapToInt(Constructor::getModifiers)
36             .anyMatch(mod -> Modifier.isProtected(mod) || Modifier.isPublic(mod)),
37             "%s must declare at least one protected or public constructor", type);
38
39         try {
40             // have a non-final non-abstract validator() method
41             final int validator;
42             try {
43                 validator = type.getMethod("validator").getModifiers();
44             } catch (NoSuchMethodException e) {
45                 throw new IllegalArgumentException(type + " must have a non-abstract non-final validator() method",
46                     e);
47             }
48             checkArgument(!Modifier.isFinal(validator), "%s must not have final validator()", type);
49
50             // have final toCanonicalString(), support(), hashCode() and equals(Object), compare(T) methods
51             checkFinalMethod(type, "toCanonicalString");
52             checkFinalMethod(type, "support");
53             checkFinalMethod(type, "hashCode");
54             checkFinalMethod(type, "equals", Object.class);
55         } catch (SecurityException e) {
56             LOG.warn("Cannot completely validate {}", type, e);
57             return Boolean.FALSE;
58         }
59
60         return Boolean.TRUE;
61     }
62
63     abstract void checkCompareTo(Class<?> type);
64
65     static void checkFinalMethod(final Class<?> type, final String name) {
66         try {
67             checkFinalMethod(type.getMethod(name).getModifiers(), type, name, "");
68         } catch (NoSuchMethodException e) {
69             throw new IllegalArgumentException(type + " must have a final " + name + "() method", e);
70         }
71     }
72
73     static void checkFinalMethod(final Class<?> type, final String name, final Class<?> arg) {
74         final String argName = arg.getSimpleName();
75         try {
76             checkFinalMethod(type.getMethod(name, arg).getModifiers(), type, name, argName);
77         } catch (NoSuchMethodException e) {
78             throw new IllegalArgumentException(type + " must have a final " + name + "(" + argName + ") method", e);
79         }
80     }
81
82     private static void checkFinalMethod(final int modifiers, final Class<?> type, final String name,
83             final String args) {
84         checkArgument(Modifier.isFinal(modifiers), "%s must have a final %s(%s) method", type, name, args);
85     }
86 }