diff options
Diffstat (limited to 'src/main/java/org/gnunet/construct/MessageLoader.java')
-rw-r--r-- | src/main/java/org/gnunet/construct/MessageLoader.java | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/src/main/java/org/gnunet/construct/MessageLoader.java b/src/main/java/org/gnunet/construct/MessageLoader.java new file mode 100644 index 0000000..71e2719 --- /dev/null +++ b/src/main/java/org/gnunet/construct/MessageLoader.java | |||
@@ -0,0 +1,215 @@ | |||
1 | /* | ||
2 | * | ||
3 | * This file is part of GNUnet. | ||
4 | * (C) 2011 Christian Grothoff (and other contributing authors) | ||
5 | * | ||
6 | * GNUnet is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published | ||
8 | * by the Free Software Foundation; either version 2, or (at your | ||
9 | * option) any later version. | ||
10 | * | ||
11 | * GNUnet is distributed in the hope that it will be useful, but | ||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with GNUnet; see the file COPYING. If not, write to the | ||
18 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
19 | * Boston, MA 02111-1307, USA. | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | package org.gnunet.construct; | ||
24 | |||
25 | |||
26 | import com.google.common.base.Charsets; | ||
27 | import org.slf4j.Logger; | ||
28 | import org.slf4j.LoggerFactory; | ||
29 | |||
30 | import java.io.BufferedReader; | ||
31 | import java.io.Closeable; | ||
32 | import java.io.IOException; | ||
33 | import java.io.InputStreamReader; | ||
34 | import java.net.URL; | ||
35 | import java.util.Enumeration; | ||
36 | import java.util.HashMap; | ||
37 | import java.util.Map; | ||
38 | |||
39 | |||
40 | /** | ||
41 | * Load message maps, which contain the information the parse/write unions. | ||
42 | */ | ||
43 | public class MessageLoader { | ||
44 | private static final Logger logger = LoggerFactory | ||
45 | .getLogger(MessageLoader.class); | ||
46 | |||
47 | |||
48 | /** | ||
49 | * Thrown when a trying to serialize an object that is not registered as a union type. | ||
50 | */ | ||
51 | public static class UnknownUnionException extends RuntimeException { | ||
52 | public UnknownUnionException(String msg) { | ||
53 | super(msg); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | |||
58 | /** | ||
59 | * Thrown when parsing a union whose ID is not known. | ||
60 | */ | ||
61 | public static class UnknownUnionIdException extends RuntimeException { | ||
62 | |||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Maps a class and tag to the corresponding union case. | ||
67 | * <p/> | ||
68 | * XXX: how much of generics is too much? | ||
69 | */ | ||
70 | private static Map<Class<? extends MessageUnion>, Map<Integer, Class<? extends MessageUnion>>> unionmap | ||
71 | = new HashMap<Class<? extends MessageUnion>, Map<Integer, Class<? extends MessageUnion>>>(100); | ||
72 | |||
73 | /* | ||
74 | * Maps a union interface and union case to the corresponding tag. | ||
75 | */ | ||
76 | private static Map<Class<? extends MessageUnion>, Map<Class<? extends MessageUnion>, Integer>> tagmap | ||
77 | = new HashMap<Class<? extends MessageUnion>, Map<Class<? extends MessageUnion>, Integer>>(100); | ||
78 | |||
79 | |||
80 | static { | ||
81 | ClassLoader classLoader = MessageLoader.class.getClassLoader(); | ||
82 | Enumeration<URL> resources; | ||
83 | try { | ||
84 | resources = classLoader.getResources("org/gnunet/construct/MsgMap.txt"); | ||
85 | } catch (IOException e) { | ||
86 | throw new RuntimeException("something went wrong with loading MsgMap.txt"); | ||
87 | } | ||
88 | |||
89 | while (resources.hasMoreElements()) { | ||
90 | loadMessageMap(resources.nextElement()); | ||
91 | } | ||
92 | |||
93 | if (tagmap.isEmpty()) { | ||
94 | logger.warn("message map empty"); | ||
95 | } | ||
96 | |||
97 | } | ||
98 | |||
99 | public static void loadMessageMap(URL loc) { | ||
100 | if (loc == null) { | ||
101 | throw new RuntimeException("could not load message map"); | ||
102 | } | ||
103 | BufferedReader in = null; | ||
104 | try { | ||
105 | in = new BufferedReader(new InputStreamReader(loc.openStream(), Charsets.UTF_8)); | ||
106 | String line; | ||
107 | while ((line = in.readLine()) != null) { | ||
108 | // skip empty lines and comments | ||
109 | if (line.isEmpty() || line.charAt(0) == '#') { | ||
110 | continue; | ||
111 | } | ||
112 | String[] m = line.split("="); | ||
113 | if (m.length != 2) { | ||
114 | throw new RuntimeException("invalid message map format (separation by '=')"); | ||
115 | } | ||
116 | String[] left = m[0].split("[|]"); | ||
117 | if (left.length != 2) { | ||
118 | logger.debug(m[0]); | ||
119 | logger.debug(m[1]); | ||
120 | logger.debug("split in " + left.length); | ||
121 | throw new RuntimeException("invalid message map format (left hand side)"); | ||
122 | } | ||
123 | int id = java.lang.Integer.parseInt(left[1].trim()); | ||
124 | String unionCaseName = m[1].trim(); | ||
125 | String unionInterfaceName = left[0]; | ||
126 | |||
127 | Class<? extends MessageUnion> unionInterface = loadClass(unionInterfaceName); | ||
128 | Class<? extends MessageUnion> unionCase = loadClass(unionCaseName); | ||
129 | |||
130 | if (!unionmap.containsKey(unionInterface)) { | ||
131 | unionmap.put(unionInterface, new HashMap<Integer, Class<? extends MessageUnion>>(5)); | ||
132 | } | ||
133 | unionmap.get(unionInterface).put(id, unionCase); | ||
134 | |||
135 | |||
136 | if (!tagmap.containsKey(unionInterface)) { | ||
137 | tagmap.put(unionInterface, new HashMap<Class<? extends MessageUnion>, Integer>(5)); | ||
138 | } | ||
139 | tagmap.get(unionInterface).put(unionCase, id); | ||
140 | |||
141 | } | ||
142 | } catch (IOException e) { | ||
143 | throw new RuntimeException("could not read message map"); | ||
144 | } finally { | ||
145 | maybeClose(in); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | private static void maybeClose(Closeable in) { | ||
150 | try { | ||
151 | if (in != null) { | ||
152 | in.close(); | ||
153 | } | ||
154 | } catch (IOException e) { | ||
155 | throw new RuntimeException("error closing stream: " + e.getMessage()); | ||
156 | } | ||
157 | } | ||
158 | |||
159 | |||
160 | @SuppressWarnings("unchecked") | ||
161 | private static Class<? extends MessageUnion> loadClass(String className) { | ||
162 | ClassLoader cl = Thread.currentThread().getContextClassLoader(); | ||
163 | Class<MessageUnion> msgClass; | ||
164 | try { | ||
165 | msgClass = (Class<MessageUnion>) cl.loadClass(className); | ||
166 | } catch (ClassNotFoundException e) { | ||
167 | throw new AssertionError(String.format("message class '%s' not found in classpath", className)); | ||
168 | } catch (ClassCastException e) { | ||
169 | throw new AssertionError(String.format("Class %s does not inherit from MessageUnion", className)); | ||
170 | } | ||
171 | return msgClass; | ||
172 | } | ||
173 | |||
174 | public static Class<? extends MessageUnion> getUnionClass(Class<? extends MessageUnion> unionInterface, int tag) { | ||
175 | Map<Integer, Class<? extends MessageUnion>> map = unionmap.get(unionInterface); | ||
176 | if (map == null) { | ||
177 | throw new UnknownUnionException("don't know how to handle unions of type '" + unionInterface + "'"); | ||
178 | } | ||
179 | |||
180 | Class<? extends MessageUnion> cls = map.get(tag); | ||
181 | if (cls == null) { | ||
182 | throw new ProtocolViolationException("don't know how to translate message of type " + tag); | ||
183 | } | ||
184 | |||
185 | return cls; | ||
186 | } | ||
187 | |||
188 | |||
189 | public static int getUnionTag(Class<? extends MessageUnion> unionInterface, Class<? extends MessageUnion> unionCase) { | ||
190 | Map<Class<? extends MessageUnion>, Integer> map = tagmap.get(unionInterface); | ||
191 | if (map == null) { | ||
192 | throw new AssertionError(String.format("%s is not a known union type", unionInterface)); | ||
193 | } | ||
194 | if (!map.containsKey(unionCase)) { | ||
195 | throw new AssertionError(String.format("%s is not a known instance of %s", unionCase, unionInterface)); | ||
196 | } | ||
197 | return map.get(unionCase); | ||
198 | } | ||
199 | |||
200 | public static void registerUnionCase(Class<? extends MessageUnion> unionInterface, | ||
201 | Class<? extends MessageUnion> unionCase, int tag) { | ||
202 | if (!unionmap.containsKey(unionInterface)) { | ||
203 | unionmap.put(unionInterface, new HashMap<Integer, Class<? extends MessageUnion>>(5)); | ||
204 | } | ||
205 | unionmap.get(unionInterface).put(tag, unionCase); | ||
206 | |||
207 | |||
208 | if (!tagmap.containsKey(unionInterface)) { | ||
209 | tagmap.put(unionInterface, new HashMap<Class<? extends MessageUnion>, Integer>(5)); | ||
210 | } | ||
211 | tagmap.get(unionInterface).put(unionCase, tag); | ||
212 | |||
213 | |||
214 | } | ||
215 | } | ||