OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 67 | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 67 Rev 80
Line 13... Line 13...
13
 
13
 
14
 package org.openconcerto.erp.modules;
14
 package org.openconcerto.erp.modules;
15
 
15
 
16
import org.openconcerto.sql.model.DBRoot;
16
import org.openconcerto.sql.model.DBRoot;
17
import org.openconcerto.sql.preferences.SQLPreferences;
17
import org.openconcerto.sql.preferences.SQLPreferences;
18
import org.openconcerto.utils.cc.IPredicate;
-
 
19
 
18
 
20
import java.io.IOException;
-
 
21
import java.io.InputStream;
19
import java.io.File;
22
import java.util.Collection;
20
import java.util.Collection;
23
import java.util.Collections;
21
import java.util.Collections;
-
 
22
import java.util.Iterator;
24
import java.util.HashMap;
23
import java.util.LinkedHashMap;
25
import java.util.Locale;
24
import java.util.List;
26
import java.util.Map;
25
import java.util.Map;
27
import java.util.Properties;
-
 
28
import java.util.ResourceBundle;
26
import java.util.ResourceBundle;
29
import java.util.prefs.Preferences;
27
import java.util.prefs.Preferences;
30
import java.util.regex.Matcher;
-
 
31
import java.util.regex.Pattern;
-
 
32
 
28
 
33
import net.jcip.annotations.ThreadSafe;
29
import net.jcip.annotations.ThreadSafe;
34
 
30
 
35
/**
31
/**
36
 * Parse module properties, and allow to create modules.
32
 * Allow to create modules.
37
 * 
33
 * 
38
 * @author Sylvain CUAZ
34
 * @author Sylvain CUAZ
39
 */
35
 */
40
@ThreadSafe
36
@ThreadSafe
41
public abstract class ModuleFactory {
37
public abstract class ModuleFactory {
42
 
38
 
43
    public static final String NAME_KEY = "name";
39
    // create an ID for each dependency
44
    public static final String DESC_KEY = "description";
40
    static protected final Map<Object, Dependency> createMap(List<Dependency> l) {
45
 
-
 
46
    protected static Properties readAndClose(final InputStream ins) throws IOException {
41
        if (l == null || l.size() == 0)
47
        final Properties props = new Properties();
42
            return Collections.<Object, Dependency> emptyMap();
48
        try {
43
        // be predictable, keep order
49
            props.load(ins);
44
        final Map<Object, Dependency> res = new LinkedHashMap<Object, Dependency>(l.size());
50
        } finally {
45
        for (final Dependency d : l) {
51
            ins.close();
46
            res.put(String.valueOf(res.size()), d);
52
        }
-
 
53
        return props;
-
 
54
    }
47
        }
55
 
-
 
56
    protected static final String getRequiredProp(Properties props, final String key) {
-
 
57
        final String res = props.getProperty(key);
-
 
58
        if (res == null)
-
 
59
            throw new IllegalStateException("Missing " + key);
-
 
60
        return res;
48
        return res;
61
    }
49
    }
62
 
50
 
63
    private static String checkMatch(final Pattern p, final String s, final String name) {
-
 
64
        if (!p.matcher(s).matches())
-
 
65
            throw new IllegalArgumentException(name + " doesn't match " + p.pattern());
-
 
66
        return s;
-
 
67
    }
-
 
68
 
-
 
69
    private static final int parseInt(Matcher m, int group) {
51
    public static final String NAME_KEY = "name";
70
        final String s = m.group(group);
-
 
71
        return s == null ? 0 : Integer.parseInt(s);
-
 
72
    }
-
 
73
 
-
 
74
    private static final ModuleVersion getVersion(Matcher m, int offset) {
-
 
75
        return new ModuleVersion(parseInt(m, offset + 1), parseInt(m, offset + 2));
-
 
76
    }
-
 
77
 
-
 
78
    private static final Pattern javaIdentifiedPatrn = Pattern.compile("\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*");
-
 
79
    private static final Pattern qualifiedPatrn = Pattern.compile(javaIdentifiedPatrn.pattern() + "(\\." + javaIdentifiedPatrn.pattern() + ")*");
-
 
80
 
-
 
81
    private static final Pattern idPatrn = qualifiedPatrn;
52
    public static final String DESC_KEY = "description";
82
    // \1 major version, \2 minor version
-
 
83
    private static final Pattern versionPatrn = Pattern.compile("(\\p{Digit}+)(?:\\.(\\p{Digit}+))?");
-
 
84
    private static final Pattern dependsSplitPatrn = Pattern.compile("\\p{Blank}*,\\p{Blank}+");
-
 
85
    // \1 id, \2 version
-
 
86
    private static final Pattern dependsPatrn = Pattern.compile("(" + idPatrn.pattern() + ")(?:\\p{Blank}+\\( *(" + versionPatrn.pattern() + ") *\\))?");
-
 
87
 
53
 
88
    private final String id;
-
 
89
    private final ModuleVersion version;
54
    private final ModuleReference ref;
90
    // TODO add moduleAPIVersion;
55
    // TODO add moduleAPIVersion;
91
    private final String contact;
56
    private final String contact;
92
    private final Map<String, IPredicate<ModuleFactory>> dependsPredicates;
-
 
93
    private final String mainClass;
-
 
94
    private ResourceBundle rsrcBundle;
-
 
95
 
-
 
96
    protected ModuleFactory(final Properties props) throws IOException {
-
 
97
        this.id = checkMatch(idPatrn, getRequiredProp(props, "id").trim(), "ID");
-
 
98
 
-
 
99
        final String version = getRequiredProp(props, "version").trim();
-
 
100
        final Matcher versionMatcher = versionPatrn.matcher(version);
-
 
101
        if (!versionMatcher.matches())
-
 
102
            throw new IllegalArgumentException("Version doesn't match " + versionPatrn.pattern());
-
 
103
        this.version = getVersion(versionMatcher, 0);
-
 
104
 
-
 
105
        this.contact = getRequiredProp(props, "contact");
-
 
106
        final String depends = props.getProperty("depends", "").trim();
-
 
107
        final String[] dependsArray = depends.length() == 0 ? new String[0] : dependsSplitPatrn.split(depends);
-
 
108
        final HashMap<String, IPredicate<ModuleFactory>> map = new HashMap<String, IPredicate<ModuleFactory>>(dependsArray.length);
-
 
109
        for (final String depend : dependsArray) {
-
 
110
            final Matcher dependMatcher = dependsPatrn.matcher(depend);
-
 
111
            if (!dependMatcher.matches())
-
 
112
                throw new IllegalArgumentException("'" + depend + "' doesn't match " + dependsPatrn.pattern());
-
 
113
            final ModuleVersion depVersion = getVersion(dependMatcher, 2);
-
 
114
            map.put(dependMatcher.group(1), new IPredicate<ModuleFactory>() {
-
 
115
                @Override
-
 
116
                public boolean evaluateChecked(ModuleFactory input) {
-
 
117
                    return input.getVersion().compareTo(depVersion) >= 0;
-
 
118
                }
-
 
119
            });
-
 
120
        }
-
 
121
        this.dependsPredicates = Collections.unmodifiableMap(map);
-
 
122
 
57
 
-
 
58
    protected ModuleFactory(final ModuleReference ref, final String contact) {
-
 
59
        if (ref.getVersion() == null)
123
        final String entryPoint = checkMatch(javaIdentifiedPatrn, props.getProperty("entryPoint", "Module"), "Entry point");
60
            throw new IllegalArgumentException("No version " + ref);
-
 
61
        this.ref = ref;
124
        this.mainClass = this.id + "." + entryPoint;
62
        this.contact = contact;
-
 
63
    }
125
 
64
 
-
 
65
    public final ModuleReference getReference() {
126
        this.rsrcBundle = null;
66
        return this.ref;
127
    }
67
    }
128
 
68
 
129
    public final String getID() {
69
    public final String getID() {
130
        return this.id;
70
        return this.getReference().getID();
131
    }
71
    }
132
 
72
 
133
    public final String getContact() {
73
    public final String getContact() {
134
        return this.contact;
74
        return this.contact;
135
    }
75
    }
136
 
76
 
137
    public final ModuleVersion getVersion() {
77
    public final ModuleVersion getVersion() {
138
        return this.version;
78
        return this.getReference().getVersion();
139
    }
79
    }
140
 
80
 
141
    public final int getMajorVersion() {
81
    public final int getMajorVersion() {
142
        return this.version.getMajor();
82
        return this.getVersion().getMajor();
143
    }
83
    }
144
 
84
 
145
    public final int getMinorVersion() {
85
    public final int getMinorVersion() {
146
        return this.version.getMinor();
86
        return this.getVersion().getMinor();
147
    }
87
    }
148
 
88
 
149
    protected final String getMainClass() {
89
    // should be immutable
150
        return this.mainClass;
90
    protected abstract Map<Object, Dependency> getDependencies();
151
    }
-
 
152
 
91
 
153
    public final Collection<String> getRequiredIDs() {
92
    public final boolean conflictsWith(final ModuleFactory f) {
-
 
93
        // a module can only be installed in one version
154
        return this.dependsPredicates.keySet();
94
        if (this.getID().equals(f.getID()))
-
 
95
            return !this.equals(f);
-
 
96
        else
-
 
97
            return this.conflictsWithOtherID(f) || f.conflictsWithOtherID(this);
155
    }
98
    }
156
 
99
 
157
    public final boolean isRequiredFactoryOK(ModuleFactory f) {
100
    // e.g. two different modules want to use the same table
158
        return this.dependsPredicates.get(f.getID()).evaluateChecked(f);
101
    protected boolean conflictsWithOtherID(final ModuleFactory f) {
-
 
102
        return false;
159
    }
103
    }
160
 
104
 
-
 
105
    public final boolean conflictsWith(final Collection<ModuleFactory> factories) {
161
    // ResourceBundle is thread-safe
106
        boolean res = false;
162
    protected synchronized final ResourceBundle getResourceBundle() {
107
        final Iterator<ModuleFactory> iter = factories.iterator();
163
        if (this.rsrcBundle == null) {
108
        while (iter.hasNext() && !res) {
164
            // don't allow classes to simplify class loaders
109
            final ModuleFactory f = iter.next();
165
            this.rsrcBundle = ResourceBundle.getBundle(getID() + ".ModuleResources", Locale.getDefault(), getRsrcClassLoader(),
-
 
166
                    ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_PROPERTIES));
110
            res = this.conflictsWith(f);
167
        }
111
        }
168
        return this.rsrcBundle;
112
        return res;
169
    }
113
    }
170
 
114
 
171
    protected abstract ClassLoader getRsrcClassLoader();
115
    protected abstract ResourceBundle getResourceBundle();
172
 
116
 
173
    public final String getName() {
117
    public final String getName() {
174
        return this.getResourceBundle().getString(NAME_KEY);
118
        return this.getResourceBundle().getString(NAME_KEY);
175
    }
119
    }
176
 
120
 
177
    public final String getDescription() {
121
    public final String getDescription() {
178
        return this.getResourceBundle().getString(DESC_KEY);
122
        return this.getResourceBundle().getString(DESC_KEY);
179
    }
123
    }
180
 
124
 
-
 
125
    /**
-
 
126
     * Create a module.
-
 
127
     * 
-
 
128
     * @param moduleDir the directory the module can write to.
-
 
129
     * @param alreadyCreated the already created modules for each dependency.
-
 
130
     * @return a new instance.
-
 
131
     * @throws Exception if the module couldn't be created.
-
 
132
     */
181
    public abstract AbstractModule createModule(Map<String, AbstractModule> alreadyCreated) throws Exception;
133
    public abstract AbstractModule createModule(final File moduleDir, final Map<Object, AbstractModule> alreadyCreated) throws Exception;
182
 
134
 
183
    // not sure if Class or Constructor are thread-safe
135
    // not sure if Class or Constructor are thread-safe
184
    protected synchronized final AbstractModule createModule(final Class<?> c) throws Exception {
136
    protected synchronized final AbstractModule createModule(final Class<?> c, final File localDir) throws Exception {
185
        return (AbstractModule) c.getConstructor(ModuleFactory.class).newInstance(this);
137
        final AbstractModule res = (AbstractModule) c.getConstructor(ModuleFactory.class).newInstance(this);
-
 
138
        res.setLocalDirectory(localDir);
-
 
139
        return res;
186
    }
140
    }
187
 
141
 
188
    public final Preferences getLocalPreferences() {
142
    public final Preferences getLocalPreferences() {
189
        return this.getPreferences(true, null);
143
        return this.getPreferences(true, null);
190
    }
144
    }
Line 192... Line 146...
192
    public final Preferences getSQLPreferences(final DBRoot root) {
146
    public final Preferences getSQLPreferences(final DBRoot root) {
193
        return this.getPreferences(false, root);
147
        return this.getPreferences(false, root);
194
    }
148
    }
195
 
149
 
196
    public final Preferences getPreferences(final boolean local, final DBRoot root) {
150
    public final Preferences getPreferences(final boolean local, final DBRoot root) {
197
        final Preferences rootPrefs = local ? Preferences.userRoot() : SQLPreferences.getMemCached(root);
151
        final Preferences rootPrefs = local ? Preferences.userRoot() : new SQLPreferences(root);
198
        // ID is a package name, transform to path to avoid bumping into the size limit
152
        // ID is a package name, transform to path to avoid bumping into the size limit
199
        return rootPrefs.node(ModulePreferencePanel.getAppPrefPath() + this.getID().replace('.', '/'));
153
        return rootPrefs.node(ModulePreferencePanel.getAppPrefPath() + this.getID().replace('.', '/'));
200
    }
154
    }
201
 
155
 
202
    @Override
156
    @Override
203
    public String toString() {
157
    public String toString() {
204
        return getClass().getSimpleName() + " " + getID() + " (" + getMajorVersion() + "." + getMinorVersion() + ")";
158
        final String className = getClass().isAnonymousClass() ? getClass().getName() : getClass().getSimpleName();
205
    }
-
 
206
 
-
 
207
    public ModuleReference getReference() {
159
        assert className.length() > 0;
208
        return new ModuleReference(this.id, this.version);
160
        return className + " " + getID() + " (" + getMajorVersion() + "." + getMinorVersion() + ")";
209
    }
161
    }
210
}
162
}