Bladeren bron

Lots of stuff

master
Chris Smith 15 jaren geleden
bovenliggende
commit
a03467739e

+ 370
- 0
src/com/dmdirc/util/ConfigFile.java Bestand weergeven

@@ -0,0 +1,370 @@
1
+/*
2
+ * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.util;
24
+
25
+import java.io.File;
26
+import java.io.FileNotFoundException;
27
+import java.io.IOException;
28
+import java.io.InputStream;
29
+import java.nio.charset.Charset;
30
+import java.util.ArrayList;
31
+import java.util.GregorianCalendar;
32
+import java.util.HashMap;
33
+import java.util.List;
34
+import java.util.Map;
35
+
36
+/**
37
+ * Reads and writes a standard DMDirc config file.
38
+ *
39
+ * @author chris
40
+ */
41
+public class ConfigFile extends TextFile {
42
+
43
+    /** A list of domains in this config file. */
44
+    private final List<String> domains = new ArrayList<String>();
45
+
46
+    /** The values associated with each flat domain. */
47
+    private final MapList<String, String> flatdomains = new MapList<String, String>();
48
+
49
+    /** The key/value sets associated with each key domain. */
50
+    private final Map<String, Map<String, String>> keydomains
51
+            = new HashMap<String, Map<String, String>>();
52
+    
53
+    /** Whether or not we should automatically create domains. */
54
+    private boolean automake;
55
+
56
+    /**
57
+     * Creates a new read-only Config File from the specified input stream.
58
+     * 
59
+     * @param is The input stream to read
60
+     */
61
+    public ConfigFile(final InputStream is) {
62
+        super(is, Charset.forName("UTF-8"));
63
+    }
64
+
65
+    /**
66
+     * Creates a new Config File from the specified file.
67
+     * 
68
+     * @param file The file to read/write
69
+     */
70
+    public ConfigFile(final File file) {
71
+        super(file, Charset.forName("UTF-8"));
72
+    }
73
+
74
+    /**
75
+     * Creates a new Config File from the specified file.
76
+     * 
77
+     * @param filename The name of the file to read/write
78
+     */
79
+    public ConfigFile(final String filename) {
80
+        this(new File(filename));
81
+    }
82
+
83
+    /**
84
+     * Sets the "automake" value of this config file. If automake is set to
85
+     * true, any calls to getKeyDomain will automatically create the domain
86
+     * if it did not previously exist.
87
+     * 
88
+     * @param automake The new value of the automake setting of this file
89
+     */
90
+    public void setAutomake(final boolean automake) {
91
+        this.automake = automake;
92
+    }
93
+
94
+    /**
95
+     * Reads the data from the file.
96
+     * 
97
+     * @throws FileNotFoundException if the file is not found
98
+     * @throws IOException if an i/o exception occured when reading
99
+     * @throws InvalidConfigFileException if the config file isn't valid
100
+     */
101
+    public void read() throws FileNotFoundException, IOException, InvalidConfigFileException {
102
+        String domain = null;
103
+        boolean keydomain = false;
104
+        int offset;
105
+        
106
+        keydomains.clear();
107
+        flatdomains.clear();
108
+        domains.clear();
109
+        
110
+        readLines();
111
+
112
+        for (String line : getLines()) {
113
+            String tline = line;
114
+            
115
+            while (!tline.isEmpty() && (tline.charAt(0) == '\t' || 
116
+                    tline.charAt(0) == ' ')) {
117
+                tline = tline.substring(1);
118
+            }
119
+
120
+            if (tline.indexOf('#') == 0 || tline.isEmpty()) {
121
+                continue;
122
+            } else if (
123
+                    (tline.endsWith(":") && !tline.endsWith("\\:"))
124
+                    && findEquals(tline) == -1) {
125
+                domain = unescape(tline.substring(0, tline.length() - 1));
126
+
127
+                domains.add(domain);
128
+
129
+                keydomain = keydomains.containsKey(domain)
130
+                        || flatdomains.containsValue("keysections", domain);
131
+                
132
+                if (keydomain && !keydomains.containsKey(domain)) {
133
+                    keydomains.put(domain, new HashMap<String, String>());
134
+                } else if (!keydomain && !flatdomains.containsKey(domain)) {
135
+                    flatdomains.add(domain);
136
+                }
137
+            } else if (domain != null && keydomain
138
+                    && (offset = findEquals(tline)) != -1) {
139
+                final String key = unescape(tline.substring(0, offset));
140
+                final String value = unescape(tline.substring(offset + 1));
141
+
142
+                keydomains.get(domain).put(key, value);
143
+            } else if (domain != null && !keydomain) {
144
+                flatdomains.add(domain, unescape(tline));
145
+            } else {
146
+                throw new InvalidConfigFileException("Unknown or unexpected" +
147
+                        " line encountered: " + tline);
148
+            }
149
+        }
150
+    }
151
+
152
+    /**
153
+     * Writes the contents of this ConfigFile to disk.
154
+     * 
155
+     * @throws IOException if the write operation fails
156
+     */
157
+    public void write() throws IOException {
158
+        if (!isWritable()) {
159
+            throw new UnsupportedOperationException("Cannot write to a file "
160
+                    + "that isn't writable");
161
+        }
162
+        
163
+        final List<String> lines = new ArrayList<String>();
164
+
165
+        lines.add("# This is a DMDirc configuration file.");
166
+        lines.add("# Written on: " + new GregorianCalendar().getTime().toString());
167
+
168
+        writeMeta(lines);
169
+
170
+        for (String domain : domains) {
171
+            if ("keysections".equals(domain)) {
172
+                continue;
173
+            }
174
+
175
+            lines.add("");
176
+
177
+            lines.add(escape(domain) + ':');
178
+
179
+            if (flatdomains.containsKey(domain)) {
180
+                for (String entry : flatdomains.get(domain)) {
181
+                    lines.add("  " + escape(entry));
182
+                }
183
+            } else {
184
+                for (Map.Entry<String, String> entry : keydomains.get(domain).entrySet()) {
185
+                    lines.add("  " + escape(entry.getKey()) + "="
186
+                            + escape(entry.getValue()));
187
+                }
188
+            }
189
+        }
190
+
191
+        writeLines(lines);
192
+    }
193
+    
194
+    /**
195
+     * Appends the meta-data (keysections) to the specified list of lines.
196
+     * 
197
+     * @param lines The set of lines to be appended to
198
+     */
199
+    private void writeMeta(final List<String> lines) {
200
+        lines.add("");
201
+        lines.add("# This section indicates which sections below take key/value");
202
+        lines.add("# pairs, rather than a simple list. It should be placed above");
203
+        lines.add("# any sections that take key/values.");
204
+        lines.add("keysections:");
205
+
206
+        for (String domain : domains) {
207
+            if ("keysections".equals(domain)) {
208
+                continue;
209
+            } else if (keydomains.containsKey(domain)) {
210
+                lines.add("  " + domain);
211
+            }
212
+        }
213
+    }
214
+    
215
+    /**
216
+     * Retrieves all the key domains for this config file.
217
+     * 
218
+     * @return This config file's key domains
219
+     */
220
+    public Map<String, Map<String, String>> getKeyDomains() {
221
+        return keydomains;
222
+    }
223
+    
224
+    /**
225
+     * Retrieves the key/values of the specified key domain.
226
+     * 
227
+     * @param domain The domain to be retrieved
228
+     * @return A map of keys to values in the specified domain
229
+     */
230
+    public Map<String, String> getKeyDomain(final String domain) {
231
+        if (automake && !isKeyDomain(domain)) {
232
+            domains.add(domain);
233
+            keydomains.put(domain, new HashMap<String, String>());
234
+        }
235
+        
236
+        return keydomains.get(domain);
237
+    }
238
+    
239
+    /**
240
+     * Retrieves the content of the specified flat domain.
241
+     * 
242
+     * @param domain The domain to be retrieved
243
+     * @return A list of lines in the specified domain
244
+     */
245
+    public List<String> getFlatDomain(final String domain) {
246
+        return flatdomains.get(domain);
247
+    }
248
+    
249
+    /**
250
+     * Determines if this config file has the specified domain.
251
+     * 
252
+     * @param domain The domain to check for
253
+     * @return True if the domain is known, false otherwise
254
+     */
255
+    public boolean hasDomain(final String domain) {
256
+        return keydomains.containsKey(domain) || flatdomains.containsKey(domain);
257
+    }
258
+
259
+    /**
260
+     * Determines if this config file has the specified domain, and the domain
261
+     * is a key domain.
262
+     * 
263
+     * @param domain The domain to check for
264
+     * @return True if the domain is known and keyed, false otherwise
265
+     */
266
+    public boolean isKeyDomain(final String domain) {
267
+        return keydomains.containsKey(domain);
268
+    }
269
+
270
+    /**
271
+     * Determines if this config file has the specified domain, and the domain
272
+     * is a flat domain.
273
+     * 
274
+     * @param domain The domain to check for
275
+     * @return True if the domain is known and flat, false otherwise
276
+     */
277
+    public boolean isFlatDomain(final String domain) {
278
+        return flatdomains.containsKey(domain);
279
+    }
280
+    
281
+    /**
282
+     * Adds a new flat domain to this config file.
283
+     * 
284
+     * @param name The name of the domain to be added
285
+     * @param data The content of the domain
286
+     */
287
+    public void addDomain(final String name, final List<String> data) {
288
+        domains.add(name);
289
+        flatdomains.add(name, data);
290
+    }
291
+
292
+    /**
293
+     * Adds a new key domain to this config file.
294
+     * 
295
+     * @param name The name of the domain to be added
296
+     * @param data The content of the domain
297
+     */    
298
+    public void addDomain(final String name, final Map<String, String> data) {
299
+        domains.add(name);
300
+        keydomains.put(name, data);
301
+    }
302
+    
303
+    /**
304
+     * Unescapes any escaped characters in the specified input string.
305
+     * 
306
+     * @param input The string to unescape
307
+     * @return The string with all escape chars (\) resolved
308
+     */
309
+    protected static String unescape(final String input) {
310
+        boolean escaped = false;
311
+        final StringBuilder temp = new StringBuilder();
312
+
313
+        for (int i = 0; i < input.length(); i++) {
314
+            final char ch = input.charAt(i);
315
+
316
+            if (escaped) {
317
+                if (ch == 'n') {
318
+                    temp.append('\n');
319
+                } else if (ch == 'r') {
320
+                    temp.append('\r');
321
+                } else {
322
+                    temp.append(ch);
323
+                }
324
+                
325
+                escaped = false;
326
+            } else if (ch == '\\') {
327
+                escaped = true;
328
+            } else {
329
+                temp.append(ch);
330
+            }
331
+        }
332
+        
333
+        return temp.toString();
334
+    }
335
+    
336
+    /**
337
+     * Escapes the specified input string by prefixing all occurances of
338
+     * \, \n, \r, =, # and : with backslashes.
339
+     * 
340
+     * @param input The string to be escaped
341
+     * @return A backslash-armoured version of the string
342
+     */
343
+    protected static String escape(final String input) {
344
+        return input.replaceAll("\\\\", "\\\\\\\\").replaceAll("\n", "\\\\n")
345
+                .replaceAll("\r", "\\\\r").replaceAll("=", "\\\\=")
346
+                .replaceAll(":", "\\\\:").replaceAll("#", "\\\\#");
347
+    }
348
+    
349
+    /**
350
+     * Finds the first non-escaped instance of '=' in the specified string.
351
+     * 
352
+     * @param input The string to be searched
353
+     * @return The offset of the first non-escaped instance of '=', or -1.
354
+     */
355
+    protected static int findEquals(final String input) {
356
+        boolean escaped = false;
357
+        
358
+        for (int i = 0; i < input.length(); i++) {
359
+            if (escaped) {
360
+                escaped = false;
361
+            } else if (input.charAt(i) == '\\') {
362
+                escaped = true;
363
+            } else if (input.charAt(i) == '=') {
364
+                return i;
365
+            }
366
+        }
367
+        
368
+        return -1;
369
+    }
370
+}

+ 47
- 0
src/com/dmdirc/util/InvalidConfigFileException.java Bestand weergeven

@@ -0,0 +1,47 @@
1
+/*
2
+ * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.util;
24
+
25
+/**
26
+ * Thrown to indicate that a config file is invalid.
27
+ * @author chris
28
+ */
29
+public class InvalidConfigFileException extends Exception {
30
+    
31
+    /**
32
+     * A version number for this class. It should be changed whenever the class
33
+     * structure is changed (or anything else that would prevent serialized
34
+     * objects being unserialized with the new class).
35
+     */    
36
+    private static final long serialVersionUID = 1;
37
+
38
+    /**
39
+     * Creates a new InvalidConfigFileException.
40
+     * 
41
+     * @param string A description of the exception that occured.
42
+     */
43
+    public InvalidConfigFileException(String string) {
44
+        super(string);
45
+    }
46
+
47
+}

+ 252
- 0
src/com/dmdirc/util/MapList.java Bestand weergeven

@@ -0,0 +1,252 @@
1
+/*
2
+ * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.util;
24
+
25
+import java.util.ArrayList;
26
+import java.util.Collection;
27
+import java.util.HashMap;
28
+import java.util.List;
29
+import java.util.Map;
30
+import java.util.Set;
31
+
32
+/**
33
+ * Wraps a Map&lt;S, List&lt;T&gt;&gt; with various convenience methods for
34
+ * accessing the data. Implements a Map-like interface for easier transition.
35
+ * 
36
+ * @param <S> the type of keys maintained by this map
37
+ * @param <T> the type of mapped values
38
+ * @author chris
39
+ */
40
+public class MapList<S,T> {
41
+    
42
+    /** Our internal map. */
43
+    protected final Map<S, List<T>> map;
44
+
45
+    /**
46
+     * Creates a new, empty MapList.
47
+     */
48
+    public MapList() {
49
+        map = new HashMap<S, List<T>>();
50
+    }
51
+
52
+    /**
53
+     * Creates a new MapList with the values from the specified list.
54
+     * 
55
+     * @param list The MapList whose values should be used
56
+     */
57
+    public MapList(final MapList<S,T> list) {
58
+        map = list.getMap();
59
+    }
60
+
61
+    /**
62
+     * Determines if this MapList is empty. An empty MapList is one that either
63
+     * contains no keys, or contains only keys which have no associated values.
64
+     * 
65
+     * @return True if this MapList is empty, false otherwise
66
+     */
67
+    public boolean isEmpty() {
68
+        for (List<T> list : map.values()) {
69
+            if (!list.isEmpty()) {
70
+                return false;
71
+            }
72
+        }
73
+        
74
+        return true;
75
+    }
76
+
77
+    /**
78
+     * Determines if this MapList contains the specified key.
79
+     * 
80
+     * @param key The key to look for
81
+     * @return True if this MapList contains the specified key, false otherwise
82
+     */
83
+    public boolean containsKey(final S key) {
84
+        return map.containsKey(key);
85
+    }
86
+
87
+    /**
88
+     * Determines if this MapList contains the specified value as a child of
89
+     * the specified key.
90
+     * 
91
+     * @param key The key to search under
92
+     * @param value The value to look for
93
+     * @return True if this MapList contains the specified key/value pair, 
94
+     * false otherwise
95
+     */
96
+    public boolean containsValue(final S key, final T value) {
97
+        return map.containsKey(key) && map.get(key).contains(value);
98
+    }
99
+
100
+    /**
101
+     * Retrieves the list of values associated with the specified key.
102
+     * 
103
+     * @param key The key whose values are being retrieved
104
+     * @return The values belonging to the specified key
105
+     */
106
+    public List<T> get(final S key) {
107
+        return map.get(key);
108
+    }
109
+    
110
+    /**
111
+     * Retrieves the value at the specified offset of the specified key.
112
+     * 
113
+     * @param key The key whose values are being retrieved
114
+     * @param index The index of the value to retrieve
115
+     * @return The specified value of the key
116
+     */    
117
+    public T get(final S key, final int index) {
118
+        return map.get(key).get(index);
119
+    }    
120
+    
121
+    /**
122
+     * Retrieves the list of values associated with the specified key, creating
123
+     * the key if neccessary.
124
+     * 
125
+     * @param key The key to retrieve
126
+     * @return A list of the specified key's values
127
+     */
128
+    public List<T> safeGet(final S key) {
129
+        if (!map.containsKey(key)) {
130
+            map.put(key, new ArrayList<T>());
131
+        }
132
+        
133
+        return map.get(key);
134
+    }
135
+    
136
+    /**
137
+     * Adds the specified key to the MapList.
138
+     * 
139
+     * @param key The key to be added
140
+     */
141
+    public void add(final S key) {
142
+        safeGet(key);
143
+    }    
144
+
145
+    /**
146
+     * Adds the specified value as a child of the specified key. If the key
147
+     * didn't previous exist, it is created.
148
+     * 
149
+     * @param key The key to which the value is being added
150
+     * @param value The value to be added
151
+     */
152
+    public void add(final S key, final T value) {
153
+        safeGet(key).add(value);
154
+    }
155
+
156
+    /**
157
+     * Adds the specified set of values to the specified key. If the key
158
+     * didn't previous exist, it is created.
159
+     * 
160
+     * @param key The key to which the value is being added
161
+     * @param values The values to be added
162
+     */    
163
+    public void add(final S key, final Collection<T> values) {
164
+        safeGet(key).addAll(values);
165
+    }    
166
+
167
+    /**
168
+     * Removes the specified key and all of its values.
169
+     * 
170
+     * @param key The key to remove
171
+     */    
172
+    public void remove(final S key) {
173
+        map.remove(key);
174
+    }
175
+    
176
+    /**
177
+     * Removes the specified value from all keys.
178
+     * 
179
+     * @param value The value to remove
180
+     */
181
+    public void removeFromAll(final T value) {
182
+        for (List<T> list : map.values()) {
183
+            list.remove(value);
184
+        }
185
+    }
186
+
187
+    /**
188
+     * Removes the specified value from the specified key.
189
+     * 
190
+     * @param key The key whose value is being removed
191
+     * @param value The value to be removed
192
+     */
193
+    public void remove(final S key, final T value) {
194
+        if (map.containsKey(key)) {
195
+            map.get(key).remove(value);
196
+        }
197
+    }    
198
+
199
+    /**
200
+     * Entirely clears this MapList.
201
+     */
202
+    public void clear() {
203
+        map.clear();
204
+    }
205
+    
206
+    /**
207
+     * Clears all values of the specified key.
208
+     * 
209
+     * @param key The key to be cleared
210
+     */
211
+    public void clear(final S key) {
212
+        safeGet(key).clear();
213
+    }    
214
+
215
+    /**
216
+     * Returns the set of all keys belonging to this MapList.
217
+     * 
218
+     * @return This MapList's keyset
219
+     */
220
+    public Set<S> keySet() {
221
+        return map.keySet();
222
+    }
223
+
224
+    /**
225
+     * Returns a collection of all values belonging to the specified key.
226
+     * 
227
+     * @param key The key whose values are being sought
228
+     * @return A collection of values belonging to the key
229
+     */
230
+    public Collection<T> values(final S key) {
231
+        return map.get(key);
232
+    }
233
+    
234
+    /**
235
+     * Retrieves the entry set for this MapList.
236
+     * 
237
+     * @return This MapList's entry set
238
+     */
239
+    public Set<Map.Entry<S, List<T>>> entrySet() {
240
+        return map.entrySet();
241
+    }
242
+    
243
+    /**
244
+     * Retrieves the map behind this maplist.
245
+     * 
246
+     * @return This MapList's map.
247
+     */
248
+    public Map<S, List<T>> getMap() {
249
+        return new HashMap<S, List<T>>(map);
250
+    }
251
+
252
+}

+ 199
- 0
src/com/dmdirc/util/TextFile.java Bestand weergeven

@@ -0,0 +1,199 @@
1
+/*
2
+ * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.util;
24
+
25
+import java.io.BufferedReader;
26
+import java.io.BufferedWriter;
27
+import java.io.File;
28
+import java.io.FileInputStream;
29
+import java.io.FileWriter;
30
+import java.io.IOException;
31
+import java.io.InputStream;
32
+import java.io.InputStreamReader;
33
+import java.nio.charset.Charset;
34
+import java.util.ArrayList;
35
+import java.util.List;
36
+
37
+/**
38
+ * Allows reading and writing to a plain text file via a list of lines.
39
+ * 
40
+ * @author chris
41
+ */
42
+public class TextFile {
43
+    
44
+    /** The file we're dealing with. */
45
+    private File file;
46
+    
47
+    /** The input stream we're dealing with. */
48
+    private InputStream is;
49
+    
50
+    /** The lines we've read from the file. */
51
+    private List<String> lines;
52
+
53
+    /** The charset to use to read the file. */
54
+    private final Charset charset;
55
+    
56
+    /**
57
+     * Creates a new instance of TextFile for the specified file, and uses the
58
+     * default charset.
59
+     * 
60
+     * @param filename The file to be read/written
61
+     */
62
+    public TextFile(final String filename) {
63
+        this(new File(filename));
64
+    }
65
+
66
+    /**
67
+     * Creates a new instance of TextFile for the specified File, and uses the
68
+     * default charset.
69
+     * 
70
+     * @param file The file to read
71
+     */
72
+    public TextFile(final File file) {
73
+        this(file, Charset.defaultCharset());
74
+    }
75
+    
76
+    /**
77
+     * Creates a new instance of TextFile for an input stream, and uses the
78
+     * default charset.
79
+     * 
80
+     * @param is The input stream to read from
81
+     */
82
+    public TextFile(final InputStream is) {
83
+        this(is, Charset.defaultCharset());
84
+    }
85
+
86
+    /**
87
+     * Creates a new instance of TextFile for the specified File, which is to
88
+     * be read using the specified charset.
89
+     *
90
+     * @param file The file to read
91
+     * @param charset The charset to read the file in
92
+     * @since 0.6.3
93
+     */
94
+    public TextFile(final File file, final Charset charset) {
95
+        this.file = file;
96
+        this.charset = charset;
97
+    }
98
+
99
+    /**
100
+     * Creates a new instance of TextFile for an input stream, which is to
101
+     * be read using the specified charset.
102
+     *
103
+     * @param is The input stream to read from
104
+     * @param charset The charset to read the file in
105
+     * @since 0.6.3
106
+     */
107
+    public TextFile(final InputStream is, final Charset charset) {
108
+        this.is = is;
109
+        this.charset = charset;
110
+    }
111
+    
112
+    /**
113
+     * Retrieves the contents of the file as a list of lines. If getLines() or
114
+     * readLines() has previously been called, a cached version is returned.
115
+     * 
116
+     * @return A list of lines in the file
117
+     * @throws IOException if an I/O exception occurs
118
+     */
119
+    public List<String> getLines() throws IOException {
120
+        if (lines == null) {
121
+            readLines();
122
+        }
123
+        
124
+        return lines;
125
+    }
126
+    
127
+    /**
128
+     * Reads the contents of the file into this TextFile's line cache.
129
+     * 
130
+     * @throws IOException If an I/O exception occurs
131
+     */
132
+    public void readLines() throws IOException {
133
+        final BufferedReader reader = new BufferedReader(
134
+                new InputStreamReader(file == null ? is : new FileInputStream(file),
135
+                charset));
136
+        lines = new ArrayList<String>();
137
+        
138
+        String line;
139
+        
140
+        while ((line = reader.readLine()) != null) {
141
+            lines.add(line);
142
+        }
143
+        
144
+        reader.close();
145
+    }
146
+    
147
+    /**
148
+     * Determines if this file is writable or not.
149
+     * 
150
+     * @return True if the file is writable, false otherwise
151
+     */
152
+    public boolean isWritable() {
153
+        return file != null;
154
+    }
155
+    
156
+    /**
157
+     * Writes the specified list of lines to the file.
158
+     * 
159
+     * @param lines The lines to be written
160
+     * @throws IOException if an I/O exception occurs
161
+     */
162
+    public void writeLines(final List<String> lines) throws IOException {
163
+        if (file == null) {
164
+            throw new UnsupportedOperationException("Cannot write to TextFile "
165
+                    + "opened with an InputStream");
166
+        }
167
+        
168
+        final BufferedWriter writer = new BufferedWriter(new FileWriter(file));
169
+        
170
+        for (String line : lines) {
171
+            writer.write(line);
172
+            writer.newLine();
173
+        }
174
+        
175
+        writer.close();
176
+    }
177
+
178
+    /**
179
+     * Retrieves the File for this TextFile, if there is one.
180
+     * 
181
+     * @return This TextFile's file, or null
182
+     */
183
+    public File getFile() {
184
+        return file;
185
+    }
186
+    
187
+    /**
188
+     * Deletes the file associated with this textfile, if there is one.
189
+     */
190
+    public void delete() {
191
+        if (file == null) {
192
+            throw new UnsupportedOperationException("Cannot delete TextFile "
193
+                    + "opened with an InputStream");
194
+        }
195
+        
196
+        file.delete();
197
+    }
198
+
199
+}

+ 3
- 1
src/com/md87/charliebravo/CharlieBravo.java Bestand weergeven

@@ -19,7 +19,8 @@ import com.dmdirc.parser.irc.callbacks.interfaces.IPost005;
19 19
  */
20 20
 public class CharlieBravo implements Runnable, IPost005, IDebugInfo, IDataIn, IDataOut {
21 21
 
22
-    protected final InputHandler handler = new InputHandler();
22
+    protected final Config config = new Config();
23
+    protected final InputHandler handler = new InputHandler(config);
23 24
     
24 25
     public void run() {
25 26
         while (true) {
@@ -41,6 +42,7 @@ public class CharlieBravo implements Runnable, IPost005, IDebugInfo, IDataIn, ID
41 42
 
42 43
     public void onPost005(final IRCParser tParser) {
43 44
         tParser.joinChannel("#MD87");
45
+        tParser.joinChannel("#MDbot");
44 46
     }
45 47
 
46 48
     public void onDebugInfo(final IRCParser tParser, final int nLevel, final String sData) {

+ 92
- 0
src/com/md87/charliebravo/Config.java Bestand weergeven

@@ -0,0 +1,92 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.charliebravo;
7
+
8
+import com.dmdirc.util.ConfigFile;
9
+import com.dmdirc.util.InvalidConfigFileException;
10
+import java.io.File;
11
+import java.io.IOException;
12
+import java.util.Arrays;
13
+import java.util.List;
14
+
15
+/**
16
+ *
17
+ * @author chris
18
+ */
19
+public class Config implements Runnable {
20
+    
21
+    private static final String FS = System.getProperty("file.separator");
22
+
23
+    private static final String FILE = System.getProperty("user.home") + FS + ".charliebravo";
24
+
25
+    private static final List<String> SETTINGS = Arrays.asList(new String[] {
26
+        "eve.apikey:String", "eve.userid:int", "eve.charid:int",
27
+        "admin.level:int",
28
+        "internal.lastseen:int", "internal.lastuser:String"
29
+    });
30
+
31
+    protected final ConfigFile configfile;
32
+
33
+    public Config() {
34
+        final File file = new File(FILE);
35
+
36
+        configfile = new ConfigFile(file);
37
+        configfile.setAutomake(true);
38
+
39
+        try {
40
+            if (file.exists()) {
41
+                configfile.read();
42
+            }
43
+        } catch (IOException ex) {
44
+            // Ignore
45
+        } catch (InvalidConfigFileException ex) {
46
+            // Ignore
47
+        }
48
+
49
+        Runtime.getRuntime().addShutdownHook(new Thread(this));
50
+    }
51
+
52
+    public boolean isLegalSetting(final String key) {
53
+        return getType(key) != null;
54
+    }
55
+
56
+    public String getType(final String key) {
57
+        for (String setting : SETTINGS) {
58
+            if (setting.startsWith(key + ":")) {
59
+                return setting.substring(setting.indexOf(':') + 1);
60
+            }
61
+        }
62
+
63
+        return null;
64
+    }
65
+
66
+    public boolean hasOption(final String user, final String key) {
67
+        return configfile.isKeyDomain(user) && configfile.getKeyDomain(user).containsKey(key);
68
+    }
69
+
70
+    public String getOption(final String user, final String key) {
71
+        return configfile.getKeyDomain(user).get(key);
72
+    }
73
+
74
+    public String setOption(final String user, final String key, final Object obj) {
75
+        final String value = String.valueOf(obj);
76
+        
77
+        if (getType(key).equals("int") && !value.matches("^[0-9]+$")) {
78
+            throw new IllegalArgumentException("That setting must be an integer");
79
+        }
80
+
81
+        return configfile.getKeyDomain(user).put(key, value);
82
+    }
83
+
84
+    public void run() {
85
+        try {
86
+            configfile.write();
87
+        } catch (IOException ex) {
88
+            // Uh oh
89
+        }
90
+    }
91
+
92
+}

+ 63
- 0
src/com/md87/charliebravo/Formatter.java Bestand weergeven

@@ -0,0 +1,63 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.charliebravo;
7
+
8
+/**
9
+ *
10
+ * @author chris
11
+ */
12
+public class Formatter {
13
+
14
+    /**
15
+     * Tests for and adds one component of the duration format.
16
+     *
17
+     * @param builder The string builder to append text to
18
+     * @param current The number of seconds in the duration
19
+     * @param duration The number of seconds in this component
20
+     * @param name The name of this component
21
+     * @return The number of seconds used by this component
22
+     */
23
+    private static int doDuration(final StringBuilder builder, final int current,
24
+            final int duration, final String name) {
25
+        int res = 0;
26
+
27
+        if (current >= duration) {
28
+            final int units = current / duration;
29
+            res = units * duration;
30
+
31
+            if (builder.length() > 0) {
32
+                builder.append(", ");
33
+            }
34
+
35
+            builder.append(units);
36
+            builder.append(' ');
37
+            builder.append(name + (units != 1 ? 's' : ""));
38
+        }
39
+
40
+        return res;
41
+    }
42
+
43
+    /**
44
+     * Formats the specified number of seconds as a string containing the
45
+     * number of days, hours, minutes and seconds.
46
+     *
47
+     * @param duration The duration in seconds to be formatted
48
+     * @return A textual version of the duration
49
+     */
50
+    public static String formatDuration(final int duration) {
51
+        final StringBuilder buff = new StringBuilder();
52
+
53
+        int seconds = duration;
54
+
55
+        seconds -= doDuration(buff, seconds, 60*60*24, "day");
56
+        seconds -= doDuration(buff, seconds, 60*60, "hour");
57
+        seconds -= doDuration(buff, seconds, 60, "minute");
58
+        seconds -= doDuration(buff, seconds, 1, "second");
59
+
60
+        return buff.toString();
61
+    }
62
+
63
+}

+ 19
- 1
src/com/md87/charliebravo/InputHandler.java Bestand weergeven

@@ -14,9 +14,14 @@ import com.dmdirc.parser.irc.callbacks.interfaces.IPrivateMessage;
14 14
 import com.md87.charliebravo.commands.AuthenticateCommand;
15 15
 import com.md87.charliebravo.commands.CalcCommand;
16 16
 import com.md87.charliebravo.commands.FollowupsCommand;
17
+import com.md87.charliebravo.commands.GitCommand;
17 18
 import com.md87.charliebravo.commands.GoogleCommand;
18 19
 import com.md87.charliebravo.commands.HelpCommand;
20
+import com.md87.charliebravo.commands.IssueCommand;
19 21
 import com.md87.charliebravo.commands.QuitCommand;
22
+import com.md87.charliebravo.commands.SetCommand;
23
+import com.md87.charliebravo.commands.TranslateCommand;
24
+import com.md87.charliebravo.commands.WhoisCommand;
20 25
 import java.util.ArrayList;
21 26
 import java.util.HashMap;
22 27
 import java.util.List;
@@ -30,16 +35,29 @@ public class InputHandler implements IChannelMessage, IPrivateMessage {
30 35
 
31 36
     protected IRCParser parser;
32 37
 
38
+    protected final Config config;
39
+
33 40
     protected final List<Command> commands = new ArrayList<Command>();
34 41
     protected final Map<String, Response> responses = new HashMap<String, Response>();
35 42
 
36
-    public InputHandler() {
43
+    public InputHandler(final Config config) {
44
+        this.config = config;
45
+        
37 46
         commands.add(new GoogleCommand());
38 47
         commands.add(new QuitCommand());
39 48
         commands.add(new HelpCommand());
40 49
         commands.add(new FollowupsCommand());
41 50
         commands.add(new AuthenticateCommand());
42 51
         commands.add(new CalcCommand());
52
+        commands.add(new WhoisCommand());
53
+        commands.add(new TranslateCommand());
54
+        commands.add(new IssueCommand());
55
+        commands.add(new GitCommand());
56
+        commands.add(new SetCommand());
57
+    }
58
+
59
+    public Config getConfig() {
60
+        return config;
43 61
     }
44 62
 
45 63
     public List<Command> getCommands() {

+ 4
- 0
src/com/md87/charliebravo/commands/AuthenticateCommand.java Bestand weergeven

@@ -32,6 +32,10 @@ public class AuthenticateCommand implements Command {
32 32
                 final String openid = result.get(1).trim();
33 33
                 handler.getParser().getClientInfoOrFake(response.getSource())
34 34
                         .getMap().put("OpenID", openid);
35
+                handler.getConfig().setOption(openid, "internal.lastseen",
36
+                        System.currentTimeMillis());
37
+                handler.getConfig().setOption(openid, "internal.lastuser",
38
+                        handler.getParser().getClientInfoOrFake(response.getSource()).toString());
35 39
                 response.sendMessage("You are now authenticated as " + openid, true);
36 40
             }
37 41
         }

+ 124
- 0
src/com/md87/charliebravo/commands/GitCommand.java Bestand weergeven

@@ -0,0 +1,124 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.charliebravo.commands;
7
+
8
+import com.dmdirc.util.Downloader;
9
+import com.md87.charliebravo.Command;
10
+import com.md87.charliebravo.Followup;
11
+import com.md87.charliebravo.InputHandler;
12
+import com.md87.charliebravo.Response;
13
+import java.net.URLEncoder;
14
+import java.nio.charset.Charset;
15
+import java.util.HashMap;
16
+import java.util.List;
17
+import java.util.Map;
18
+import java.util.regex.Matcher;
19
+import java.util.regex.Pattern;
20
+
21
+/**
22
+ *
23
+ * @author chris
24
+ */
25
+public class GitCommand implements Command {
26
+
27
+    public void execute(InputHandler handler, Response response, String line) throws Exception {
28
+        if (line.isEmpty()) {
29
+            response.sendMessage("You need to specify a revision", true);
30
+        } else {
31
+            final List<String> result = Downloader.getPage(
32
+                    "http://git.dmdirc.com/cgit.cgi/client/commit/?id="
33
+                    + URLEncoder.encode(line, Charset.defaultCharset().name()));
34
+            final StringBuilder builder = new StringBuilder();
35
+
36
+            for (String resline : result) {
37
+                builder.append(resline);
38
+            }
39
+
40
+            if (builder.indexOf("<div class=\"error\">Bad object id:") > -1) {
41
+                response.sendMessage("That commit was not found", true);
42
+            } else {
43
+                Matcher matcher = Pattern.compile("<th>author</th>"
44
+                        + "<td>(.*?) &lt;(.*?)&gt;</td><td class='right'>(.*?)</td>")
45
+                        .matcher(builder);
46
+                matcher.find();
47
+                final String authorName = matcher.group(1);
48
+                final String authorEmail = matcher.group(2);
49
+                final String commitDate = matcher.group(3);
50
+
51
+                matcher = Pattern.compile("<th>commit</th><td colspan='2' class='sha1'>"
52
+                        + "<a href='/cgit.cgi/client/commit/\\?id=(.*?)'>").matcher(builder);
53
+                matcher.find();
54
+                final String commitHash = matcher.group(1);
55
+
56
+                matcher = Pattern.compile("<div class='commit-subject'>(.*?)</div>")
57
+                        .matcher(builder);
58
+                matcher.find();
59
+                final String commitSubject = matcher.group(1);
60
+                
61
+                matcher = Pattern.compile("<div class='diffstat-summary'>(.*?)</div>")
62
+                        .matcher(builder);
63
+                matcher.find();
64
+                final String commitDiff = matcher.group(1);
65
+
66
+                response.sendMessage("commit " + commitHash + " was made by "
67
+                        + authorName + " with message '" + commitSubject + "'");
68
+                response.addFollowup(new DiffFollowup(commitDiff, commitHash));
69
+                response.addFollowup(new DetailsFollowup(authorName, authorEmail,
70
+                        commitHash, commitDate));
71
+            }
72
+            
73
+        }
74
+
75
+    }
76
+
77
+    protected static class DiffFollowup implements Followup {
78
+
79
+        private final String data;
80
+        private final String hash;
81
+
82
+        public DiffFollowup(String data, String hash) {
83
+            this.data = data;
84
+            this.hash = hash;
85
+        }
86
+
87
+        public boolean matches(String line) {
88
+            return line.equalsIgnoreCase("diff");
89
+        }
90
+
91
+        public void execute(InputHandler handler, Response response, String line) throws Exception {
92
+            response.setInheritFollows(true);
93
+            response.sendMessage("there were " + data.replaceAll("^(.*), (.*?)$", "$1 and $2")
94
+                    + " in commit "
95
+                    + hash + ". See http://git.dmdirc.com/cgit.cgi/client/diff/?id=" + hash);
96
+        }
97
+
98
+    }
99
+
100
+    protected static class DetailsFollowup implements Followup {
101
+
102
+        private final String authorName, authorEmail, hash, time;
103
+
104
+        public DetailsFollowup(String authorName, String authorEmail, String hash, String time) {
105
+            this.authorName = authorName;
106
+            this.authorEmail = authorEmail;
107
+            this.hash = hash;
108
+            this.time = time;
109
+        }
110
+
111
+        public boolean matches(String line) {
112
+            return line.equalsIgnoreCase("details");
113
+        }
114
+
115
+        public void execute(InputHandler handler, Response response, String line) throws Exception {
116
+            response.setInheritFollows(true);
117
+            response.sendMessage("commit " + hash + " was made on " + time + " by "
118
+                    + authorName + " <" + authorEmail + ">. " +
119
+                    "See http://git.dmdirc.com/cgit.cgi/client/commit/?id=" + hash);
120
+        }
121
+
122
+    }
123
+
124
+}

+ 86
- 0
src/com/md87/charliebravo/commands/IssueCommand.java Bestand weergeven

@@ -0,0 +1,86 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.charliebravo.commands;
7
+
8
+import com.dmdirc.util.Downloader;
9
+import com.md87.charliebravo.Command;
10
+import com.md87.charliebravo.Followup;
11
+import com.md87.charliebravo.InputHandler;
12
+import com.md87.charliebravo.Response;
13
+import java.util.HashMap;
14
+import java.util.List;
15
+import java.util.Map;
16
+import java.util.regex.Matcher;
17
+import java.util.regex.Pattern;
18
+
19
+/**
20
+ *
21
+ * @author chris
22
+ */
23
+public class IssueCommand implements Command {
24
+
25
+    public void execute(InputHandler handler, Response response, String line) throws Exception {
26
+        if (line.isEmpty() || !line.matches("^[0-9]+$")) {
27
+            response.sendMessage("You need to specify an issue number", true);
28
+        } else {
29
+            final List<String> result = Downloader.getPage("http://bugs.dmdirc.com/view.php?id="
30
+                    + line);
31
+            final StringBuilder builder = new StringBuilder();
32
+
33
+            for (String resline : result) {
34
+                builder.append(resline);
35
+            }
36
+
37
+            if (builder.indexOf("APPLICATION ERROR #1100") > -1) {
38
+                response.sendMessage("That issue was not found", true);
39
+            } else if (builder.indexOf("<p>Access Denied.</p>") > -1) {
40
+                response.sendMessage("that issue is private. Please see "
41
+                        + "http://bugs.dmdirc.com/view/" + line);
42
+            } else {
43
+                final Map<String, String> data = new HashMap<String, String>();
44
+
45
+                final Pattern pattern = Pattern.compile(
46
+                        "<td class=\"category\".*?>\\s*(.*?)\\s*"
47
+                        + "</td>\\s*(?:<!--.*?-->\\s*)?<td.*?>\\s*(.*?)\\s*</td>",
48
+                        Pattern.CASE_INSENSITIVE + Pattern.DOTALL);
49
+                final Matcher matcher = pattern.matcher(builder);
50
+
51
+                while (matcher.find()) {
52
+                    data.put(matcher.group(1).toLowerCase(), matcher.group(2));
53
+                }
54
+
55
+                response.sendMessage("issue " + data.get("id") + " is \""
56
+                        + data.get("summary").substring(9) + "\". Current "
57
+                        + "status is " + data.get("status") + " ("
58
+                        + data.get("resolution") + ")");
59
+                response.addFollowup(new IssueFollowup(data));
60
+            }
61
+            
62
+        }
63
+
64
+    }
65
+
66
+    protected static class IssueFollowup implements Followup {
67
+
68
+        private final Map<String, String> data;
69
+
70
+        public IssueFollowup(Map<String, String> data) {
71
+            this.data = data;
72
+        }
73
+
74
+        public boolean matches(String line) {
75
+            return data.containsKey(line.toLowerCase());
76
+        }
77
+
78
+        public void execute(InputHandler handler, Response response, String line) throws Exception {
79
+            response.setInheritFollows(true);
80
+            response.sendMessage("the " + line.toLowerCase() + " of issue "
81
+                    + data.get("id") + " is: " + data.get(line.toLowerCase()));
82
+        }
83
+
84
+    }
85
+
86
+}

+ 40
- 0
src/com/md87/charliebravo/commands/SetCommand.java Bestand weergeven

@@ -0,0 +1,40 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.charliebravo.commands;
7
+
8
+import com.md87.charliebravo.Command;
9
+import com.md87.charliebravo.InputHandler;
10
+import com.md87.charliebravo.Response;
11
+
12
+/**
13
+ *
14
+ * @author chris
15
+ */
16
+public class SetCommand implements Command {
17
+
18
+    public void execute(InputHandler handler, Response response, String line) throws Exception {
19
+        final String openID = (String) handler.getParser().getClientInfoOrFake(response.getSource())
20
+                .getMap().get("OpenID");
21
+
22
+        final String[] parts = line.split("\\s+", 2);
23
+
24
+        if (openID == null) {
25
+            response.sendMessage("You must be authorised to use this command", true);
26
+        } else if (parts.length < 2) {
27
+            response.sendMessage("You must specify a setting name and value", true);
28
+        } else if (!handler.getConfig().isLegalSetting(parts[0])) {
29
+            response.sendMessage("That isn't a legal setting", true);
30
+        } else if (parts[0].startsWith("admin.")
31
+                && (!handler.getConfig().hasOption(openID, "admin.level")
32
+                || Integer.parseInt(handler.getConfig().getOption(openID, "admin.level")) < 100)) {
33
+            response.sendMessage("You do not have sufficient access", true);
34
+        } else {
35
+            handler.getConfig().setOption(openID, parts[0], parts[1]);
36
+            response.sendMessage("OK", true);
37
+        }
38
+    }
39
+
40
+}

+ 188
- 0
src/com/md87/charliebravo/commands/TranslateCommand.java Bestand weergeven

@@ -0,0 +1,188 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.charliebravo.commands;
7
+
8
+import com.md87.charliebravo.Command;
9
+import com.md87.charliebravo.Followup;
10
+import com.md87.charliebravo.InputHandler;
11
+import com.md87.charliebravo.Response;
12
+import java.io.BufferedReader;
13
+import java.io.IOException;
14
+import java.io.InputStreamReader;
15
+import java.net.MalformedURLException;
16
+import java.net.URL;
17
+import java.net.URLConnection;
18
+import java.net.URLEncoder;
19
+import java.nio.charset.Charset;
20
+import org.json.JSONException;
21
+import org.json.JSONObject;
22
+
23
+/**
24
+ *
25
+ * @author chris
26
+ */
27
+public class TranslateCommand implements Command {
28
+
29
+    protected static final String[][] LANGUAGES = {
30
+        {"AFRIKAANS", "af"},
31
+        {"ALBANIAN", "sq"},
32
+        {"AMHARIC", "am"},
33
+        {"ARABIC", "ar"},
34
+        {"ARMENIAN", "hy"},
35
+        {"AZERBAIJANI", "az"},
36
+        {"BASQUE", "eu"},
37
+        {"BELARUSIAN", "be"},
38
+        {"BENGALI", "bn"},
39
+        {"BIHARI", "bh"},
40
+        {"BULGARIAN", "bg"},
41
+        {"BURMESE", "my"},
42
+        {"CATALAN", "ca"},
43
+        {"CHEROKEE", "chr"},
44
+        {"CHINESE", "zh"},
45
+        {"CHINESE (SIMPLIFIED)", "zh-CN"},
46
+        {"CHINESE (TRADITIONAL)", "zh-TW"},
47
+        {"SIMPLIFIED CHINESE", "zh-CN"},
48
+        {"TRADITIONAL CHINESE", "zh-TW"},
49
+        {"CROATIAN", "hr"},
50
+        {"CZECH", "cs"},
51
+        {"DANISH", "da"},
52
+        {"DHIVEHI", "dv"},
53
+        {"DUTCH", "nl"},
54
+        {"ENGLISH", "en"},
55
+        {"ESPERANTO", "eo"},
56
+        {"ESTONIAN", "et"},
57
+        {"FILIPINO", "tl"},
58
+        {"FINNISH", "fi"},
59
+        {"FRENCH", "fr"},
60
+        {"GALICIAN", "gl"},
61
+        {"GEORGIAN", "ka"},
62
+        {"GERMAN", "de"},
63
+        {"GREEK", "el"},
64
+        {"GUARANI", "gn"},
65
+        {"GUJARATI", "gu"},
66
+        {"HEBREW", "iw"},
67
+        {"HINDI", "hi"},
68
+        {"HUNGARIAN", "hu"},
69
+        {"ICELANDIC", "is"},
70
+        {"INDONESIAN", "id"},
71
+        {"INUKTITUT", "iu"},
72
+        {"ITALIAN", "it"},
73
+        {"JAPANESE", "ja"},
74
+        {"KANNADA", "kn"},
75
+        {"KAZAKH", "kk"},
76
+        {"KHMER", "km"},
77
+        {"KOREAN", "ko"},
78
+        {"KURDISH", "ku"},
79
+        {"KYRGYZ", "ky"},
80
+        {"LAOTHIAN", "lo"},
81
+        {"LATVIAN", "lv"},
82
+        {"LITHUANIAN", "lt"},
83
+        {"MACEDONIAN", "mk"},
84
+        {"MALAY", "ms"},
85
+        {"MALAYALAM", "ml"},
86
+        {"MALTESE", "mt"},
87
+        {"MARATHI", "mr"},
88
+        {"MONGOLIAN", "mn"},
89
+        {"NEPALI", "ne"},
90
+        {"NORWEGIAN", "no"},
91
+        {"ORIYA", "or"},
92
+        {"PASHTO", "ps"},
93
+        {"PERSIAN", "fa"},
94
+        {"POLISH", "pl"},
95
+        {"PORTUGUESE", "pt-PT"},
96
+        {"PUNJABI", "pa"},
97
+        {"ROMANIAN", "ro"},
98
+        {"RUSSIAN", "ru"},
99
+        {"SANSKRIT", "sa"},
100
+        {"SERBIAN", "sr"},
101
+        {"SINDHI", "sd"},
102
+        {"SINHALESE", "si"},
103
+        {"SLOVAK", "sk"},
104
+        {"SLOVENIAN", "sl"},
105
+        {"SPANISH", "es"},
106
+        {"SWAHILI", "sw"},
107
+        {"SWEDISH", "sv"},
108
+        {"TAJIK", "tg"},
109
+        {"TAMIL", "ta"},
110
+        {"TAGALOG", "tl"},
111
+        {"TELUGU", "te"},
112
+        {"THAI", "th"},
113
+        {"TIBETAN", "bo"},
114
+        {"TURKISH", "tr"},
115
+        {"UKRAINIAN", "uk"},
116
+        {"URDU", "ur"},
117
+        {"UZBEK", "uz"},
118
+        {"UIGHUR", "ug"},
119
+        {"VIETNAMESE", "vi"},
120
+    };
121
+
122
+    public void execute(final InputHandler handler, Response response, String line) throws MalformedURLException, IOException, JSONException {
123
+        String target = "en";
124
+        String text = line;
125
+        int offset;
126
+
127
+        if ((offset = text.lastIndexOf(" into ")) > -1) {
128
+            final String lang = text.substring(offset + 6);
129
+
130
+            for (String[] pair : LANGUAGES) {
131
+                if (pair[0].equalsIgnoreCase(lang)) {
132
+                    target = pair[1];
133
+                    text = text.substring(0, offset);
134
+                }
135
+            }
136
+        }
137
+
138
+        URL url = new URL("http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&langpair=%7C" + target + "&q=" +
139
+                URLEncoder.encode(text, Charset.defaultCharset().name()));
140
+        URLConnection connection = url.openConnection();
141
+        connection.addRequestProperty("Referer", "http://chris.smith.name/");
142
+
143
+        String input;
144
+        StringBuilder builder = new StringBuilder();
145
+        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
146
+        while((input = reader.readLine()) != null) {
147
+            builder.append(input);
148
+        }
149
+
150
+        JSONObject json = new JSONObject(builder.toString());
151
+        if (json.getInt("responseStatus") != 200) {
152
+            throw new IOException(json.getString("responseDetails"));
153
+        }
154
+
155
+        response.sendMessage("that translates to \""
156
+                + json.getJSONObject("responseData").getString("translatedText") + "\"");
157
+        response.addFollowup(new LanguageFollowup(json.getJSONObject("responseData")));
158
+    }
159
+
160
+    protected static class LanguageFollowup implements Followup {
161
+
162
+        private final JSONObject result;
163
+
164
+        public LanguageFollowup(JSONObject result) {
165
+            this.result = result;
166
+        }
167
+
168
+        public boolean matches(String line) {
169
+            return line.equals("language");
170
+        }
171
+
172
+        public void execute(final InputHandler handler, Response response, String line) throws Exception {
173
+            final String target = result.getString("detectedSourceLanguage");
174
+            for (String[] pair : LANGUAGES) {
175
+                if (pair[1].equalsIgnoreCase(target)) {
176
+                    response.sendMessage("the language was detected as "
177
+                            + pair[0].charAt(0) + pair[0].substring(1).toLowerCase());
178
+                    return;
179
+                }
180
+            }
181
+            
182
+            response.sendMessage("the language was detected as '"
183
+                    + target + "', but I don't know what that is");
184
+        }
185
+
186
+    }
187
+
188
+}

+ 67
- 0
src/com/md87/charliebravo/commands/WhoisCommand.java Bestand weergeven

@@ -0,0 +1,67 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.charliebravo.commands;
7
+
8
+import com.dmdirc.parser.irc.ClientInfo;
9
+import com.md87.charliebravo.Command;
10
+import com.md87.charliebravo.Formatter;
11
+import com.md87.charliebravo.InputHandler;
12
+import com.md87.charliebravo.Response;
13
+
14
+/**
15
+ *
16
+ * @author chris
17
+ */
18
+public class WhoisCommand implements Command {
19
+
20
+    @SuppressWarnings("unchecked")
21
+    public void execute(InputHandler handler, Response response, String line) throws Exception {
22
+        if (line.isEmpty()) {
23
+            response.sendRawMessage("Who would you like to whois, " + response.getSource() + "?");
24
+        } else {
25
+            final ClientInfo ci = handler.getParser().getClientInfo(line);
26
+
27
+            if (ci == null) {
28
+                if (handler.getConfig().hasOption(line, "internal.lastseen")) {
29
+                    final StringBuilder extra = new StringBuilder();
30
+
31
+                    if (handler.getConfig().hasOption(line, "admin.level")) {
32
+                        extra.append(", and has access level ");
33
+                        extra.append(handler.getConfig().getOption(line, "admin.level"));
34
+                    }
35
+
36
+                    response.sendMessage(line + " last authenticated with me "
37
+                            + Formatter.formatDuration((int)
38
+                            (System.currentTimeMillis() -
39
+                            Long.valueOf(handler.getConfig().getOption(line, "internal.lastseen")))
40
+                            / 1000)
41
+                            + " ago" + extra);
42
+                } else {
43
+                    response.sendMessage("I am not aware of anyone by that name", true);
44
+                }
45
+            } else {
46
+                final String openid = (String) ci.getMap().get("OpenID");
47
+
48
+                if (openid == null) {
49
+                    response.sendMessage(ci.getNickname() + " has not authenticated with me", true);
50
+                } else {
51
+                    final StringBuilder extra = new StringBuilder();
52
+
53
+                    if (handler.getConfig().hasOption(openid, "admin.level")) {
54
+                        extra.append(", and has access level ");
55
+                        extra.append(handler.getConfig().getOption(openid, "admin.level"));
56
+                    }
57
+
58
+                    response.sendMessage((ci.getNickname().equals(response.getSource()) ?
59
+                            "you are " : ci.getNickname() + " is")
60
+                            + " authenticated as " + openid + extra);
61
+                }
62
+            }
63
+        }
64
+
65
+    }
66
+
67
+}

Laden…
Annuleren
Opslaan