com.strider.datadefender.requirement.registry.ClassAndFunctionRegistry   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 61
c 2
b 0
f 0
dl 0
loc 119
ccs 0
cts 38
cp 0
rs 10
wmc 18

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getFunctionsSingleton(Class) 0 2 1
A getNameForClass(Class) 0 6 2
B getClassForName(String) 0 15 6
A clearAutoResolvePackages() 0 2 1
A singleton() 0 2 1
A registerFunctions(Requirement) 0 31 3
A registerAutoResolvePackage(String) 0 3 1
A initialize(IDbFactory) 0 5 3
1
/*
2
 * Copyright 2014, Armenak Grigoryan, and individual contributors as indicated
3
 * by the @authors tag. See the copyright.txt in the distribution for a
4
 * full listing of individual contributors.
5
 *
6
 * This is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as
8
 * published by the Free Software Foundation; either version 2.1 of
9
 * the License, or (at your option) any later version.
10
 *
11
 * This software is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
 * Lesser General Public License for more details.
15
 */
16
package com.strider.datadefender.requirement.registry;
17
18
import com.mchange.v1.lang.ClassUtils;
19
import com.strider.datadefender.database.IDbFactory;
20
import com.strider.datadefender.requirement.Requirement;
21
22
import java.lang.reflect.InvocationTargetException;
23
import java.lang.reflect.Method;
24
import java.lang.reflect.Modifier;
25
import java.util.HashMap;
26
import java.util.LinkedHashSet;
27
import java.util.List;
28
import java.util.Map;
29
import java.util.Set;
30
import java.util.stream.Collectors;
31
import java.util.stream.Stream;
32
33
import lombok.RequiredArgsConstructor;
34
import lombok.extern.log4j.Log4j2;
35
import org.apache.commons.collections4.CollectionUtils;
36
37
/**
38
 * Very basic registry for classes that need to be instantiated for use by
39
 * requirement functions.
40
 *
41
 * @author Zaahid Bateson <[email protected]>
42
 */
43
@RequiredArgsConstructor
44
@Log4j2
45
public class ClassAndFunctionRegistry {
46
47
    private static ClassAndFunctionRegistry instance = new ClassAndFunctionRegistry();
48
49
    private Map<Class<?>, RequirementFunction> singletons = new HashMap<>();
50
    private Set<String> autoResolvePackages = new LinkedHashSet<>();
51
52
    public RequirementFunction getFunctionsSingleton(Class<?> cls) {
53
        return singletons.get(cls);
54
    }
55
56
    /**
57
     * Clears any set auto resolve packages on the singleton instance.
58
     */
59
    public void clearAutoResolvePackages() {
60
        autoResolvePackages.clear();
61
    }
62
63
    /**
64
     * Registers the passed package name as auto-resolvable, so classes under it
65
     * don't need to be fully qualified.
66
     *
67
     * @param name
68
     */
69
    public void registerAutoResolvePackage(String name) {
70
        log.debug("Registering autoresolve package {}", name);
71
        autoResolvePackages.add(name);
72
    }
73
74
    /**
75
     * Returns a Class object for the passed name, using autoresolve packages if
76
     * the name of the passed class starts with an uppercase letter, and does
77
     * not contain any "." characters.
78
     * 
79
     * This means inner classes can't be autoresolved.
80
     *
81
     * @param className
82
     * @return
83
     */
84
    public Class<?> getClassForName(String className) throws ClassNotFoundException {
85
        if (!className.contains(".") && Character.isUpperCase(className.charAt(0))) {
86
            for (String pkg : autoResolvePackages) {
87
                try {
88
                    Class<?> c = ClassUtils.forName(pkg + "." + className);
89
                    if (Modifier.isPublic(c.getModifiers())) {
90
                        log.debug("{} class autoresolved to {}", () -> className, () -> pkg + "." + className);
91
                        return c;
92
                    }
93
                } catch (ClassNotFoundException e) {
94
                    log.debug("Class with name {} not found while autoresolving", () -> pkg + "." + className);
95
                }
96
            }
97
        }
98
        return ClassUtils.forName(className);
99
    }
100
101
    /**
102
     * If the passed Class's package is defined in an autoresolved package, the
103
     * shortened name of the class is returned without the package name.
104
     *
105
     * @param className
106
     * @return
107
     */
108
    public String getNameForClass(Class<?> clazz) {
109
110
        if (autoResolvePackages.contains(clazz.getPackageName())) {
111
            return clazz.getCanonicalName().replace(clazz.getPackageName() + ".", "");
112
        }
113
        return clazz.getCanonicalName();
114
    }
115
116
    public void registerFunctions(Requirement requirements)
117
        throws NoSuchMethodException,
118
        InstantiationException,
119
        IllegalAccessException,
120
        IllegalArgumentException,
121
        InvocationTargetException {
122
123
        log.debug("Number of tables: {}", () -> CollectionUtils.emptyIfNull(requirements.getTables()).size());
124
        List<Method> fns = Stream.concat(
125
            requirements.getTables().stream()
126
                .flatMap((t) -> t.getColumns().stream())
127
                .flatMap((c) -> c.getResolvedPlan().getFunctions().stream()),
128
            requirements.getTables().stream()
129
                .flatMap((t) -> t.getColumns().stream())
130
                .map((c) -> c.getResolvedPlan().getCombiner())
131
        )
132
            .filter((fn) -> fn != null)     // combiner could be null
133
            .map((fn) -> fn.getFunction())
134
            .collect(Collectors.toList());
135
136
        log.debug("Found {} classes to register with functions for anonymization", fns.size());
137
        for (Method m : fns) {
138
            Class<?> cls = m.getDeclaringClass();
139
            log.debug(
140
                "Class: {}, is a RequirementFunctionClass: {}",
141
                () -> cls.getName(),
142
                () -> RequirementFunction.class.isAssignableFrom(cls)
143
            );
144
            if (RequirementFunction.class.isAssignableFrom(cls)) {
145
                log.debug("Registering RequirementFunctionClass: {}", cls);
146
                singletons.put(cls, (RequirementFunction) cls.getDeclaredConstructor().newInstance());
147
            }
148
        }
149
    }
150
151
    public void initialize(IDbFactory factory) {
152
        for (Object o : singletons.values()) {
153
            if (o instanceof DatabaseAwareRequirementFunction) {
154
                log.debug("Initializing DatabaseAwareRequirementFunctionClass for: {}", o.getClass());
155
                ((DatabaseAwareRequirementFunction) o).initialize(factory);
156
            }
157
        }
158
    }
159
160
    public static ClassAndFunctionRegistry singleton() {
161
        return instance;
162
    }
163
}
164