3438 |
04 Jun 07 |
nicklas |
1 |
/* |
3438 |
04 Jun 07 |
nicklas |
$Id $ |
3438 |
04 Jun 07 |
nicklas |
3 |
|
3675 |
16 Aug 07 |
jari |
Copyright (C) 2007 Nicklas Nordborg, Martin Svensson |
3438 |
04 Jun 07 |
nicklas |
5 |
|
3438 |
04 Jun 07 |
nicklas |
This file is part of BASE - BioArray Software Environment. |
3438 |
04 Jun 07 |
nicklas |
Available at http://base.thep.lu.se/ |
3438 |
04 Jun 07 |
nicklas |
8 |
|
3438 |
04 Jun 07 |
nicklas |
BASE is free software; you can redistribute it and/or |
3438 |
04 Jun 07 |
nicklas |
modify it under the terms of the GNU General Public License |
4479 |
05 Sep 08 |
jari |
as published by the Free Software Foundation; either version 3 |
3438 |
04 Jun 07 |
nicklas |
of the License, or (at your option) any later version. |
3438 |
04 Jun 07 |
nicklas |
13 |
|
3438 |
04 Jun 07 |
nicklas |
BASE is distributed in the hope that it will be useful, |
3438 |
04 Jun 07 |
nicklas |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
3438 |
04 Jun 07 |
nicklas |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3438 |
04 Jun 07 |
nicklas |
GNU General Public License for more details. |
3438 |
04 Jun 07 |
nicklas |
18 |
|
3438 |
04 Jun 07 |
nicklas |
You should have received a copy of the GNU General Public License |
4515 |
11 Sep 08 |
jari |
along with BASE. If not, see <http://www.gnu.org/licenses/>. |
3438 |
04 Jun 07 |
nicklas |
21 |
*/ |
3438 |
04 Jun 07 |
nicklas |
22 |
package net.sf.basedb.util; |
3438 |
04 Jun 07 |
nicklas |
23 |
|
3438 |
04 Jun 07 |
nicklas |
24 |
import java.util.Collection; |
3438 |
04 Jun 07 |
nicklas |
25 |
import java.util.HashSet; |
3438 |
04 Jun 07 |
nicklas |
26 |
import java.util.LinkedList; |
3438 |
04 Jun 07 |
nicklas |
27 |
import java.util.List; |
3438 |
04 Jun 07 |
nicklas |
28 |
import java.util.Set; |
3438 |
04 Jun 07 |
nicklas |
29 |
|
3438 |
04 Jun 07 |
nicklas |
30 |
import net.sf.basedb.core.DbControl; |
3438 |
04 Jun 07 |
nicklas |
31 |
import net.sf.basedb.core.ItemQuery; |
6372 |
06 Dec 13 |
nicklas |
32 |
import net.sf.basedb.core.Permission; |
3438 |
04 Jun 07 |
nicklas |
33 |
import net.sf.basedb.core.PluginConfiguration; |
3438 |
04 Jun 07 |
nicklas |
34 |
import net.sf.basedb.core.PluginDefinition; |
3438 |
04 Jun 07 |
nicklas |
35 |
import net.sf.basedb.core.SessionControl; |
3438 |
04 Jun 07 |
nicklas |
36 |
import net.sf.basedb.core.Type; |
3438 |
04 Jun 07 |
nicklas |
37 |
import net.sf.basedb.core.plugin.GuiContext; |
3438 |
04 Jun 07 |
nicklas |
38 |
import net.sf.basedb.core.plugin.InteractivePlugin; |
4594 |
21 Oct 08 |
nicklas |
39 |
import net.sf.basedb.util.filter.Filter; |
3438 |
04 Jun 07 |
nicklas |
40 |
|
3438 |
04 Jun 07 |
nicklas |
41 |
/** |
3438 |
04 Jun 07 |
nicklas |
A class with utility methods for plug-ins. |
3438 |
04 Jun 07 |
nicklas |
43 |
|
3438 |
04 Jun 07 |
nicklas |
@author Nicklas |
3438 |
04 Jun 07 |
nicklas |
@version 2.4 |
3675 |
16 Aug 07 |
jari |
@base.modified $Date$ |
3438 |
04 Jun 07 |
nicklas |
47 |
*/ |
3438 |
04 Jun 07 |
nicklas |
48 |
public class ContextUtil |
3438 |
04 Jun 07 |
nicklas |
49 |
{ |
3438 |
04 Jun 07 |
nicklas |
50 |
|
3438 |
04 Jun 07 |
nicklas |
51 |
/** |
3438 |
04 Jun 07 |
nicklas |
Call the {@link InteractivePlugin#isInContext(GuiContext, Object)} method |
3438 |
04 Jun 07 |
nicklas |
for all plug-ins returned by the queries. |
3438 |
04 Jun 07 |
nicklas |
54 |
|
3438 |
04 Jun 07 |
nicklas |
@param dc The DbControl to use for database access |
3438 |
04 Jun 07 |
nicklas |
@param pluginQuery A query returning plugin definition items |
3438 |
04 Jun 07 |
nicklas |
@param configQuery A query returning plugin configuration items. The query must |
3438 |
04 Jun 07 |
nicklas |
have defined a parameter <code>plugin</code> which restricts the query |
3438 |
04 Jun 07 |
nicklas |
to only return configurations for a particular plugin definition |
3438 |
04 Jun 07 |
nicklas |
@param context The current context |
3438 |
04 Jun 07 |
nicklas |
@param currentItem The current item, or null if used from a list context |
3438 |
04 Jun 07 |
nicklas |
@return A list of {@link ContextResult} objects |
3438 |
04 Jun 07 |
nicklas |
63 |
*/ |
3438 |
04 Jun 07 |
nicklas |
64 |
public static List<ContextResult> checkContext(DbControl dc, ItemQuery<PluginDefinition> pluginQuery, |
3438 |
04 Jun 07 |
nicklas |
65 |
ItemQuery<PluginConfiguration> configQuery, GuiContext context, Object currentItem) |
3438 |
04 Jun 07 |
nicklas |
66 |
{ |
3438 |
04 Jun 07 |
nicklas |
67 |
List<ContextResult> results = new LinkedList<ContextResult>(); |
3438 |
04 Jun 07 |
nicklas |
68 |
SessionControl sc = dc.getSessionControl(); |
3438 |
04 Jun 07 |
nicklas |
69 |
List<PluginDefinition> plugins = pluginQuery.list(dc); |
3438 |
04 Jun 07 |
nicklas |
70 |
for (PluginDefinition plugin : plugins) |
3438 |
04 Jun 07 |
nicklas |
71 |
{ |
6372 |
06 Dec 13 |
nicklas |
// Skip plug-in if user doesn't have USE Permission for it |
6372 |
06 Dec 13 |
nicklas |
73 |
if (!plugin.hasPermission(Permission.USE)) |
6372 |
06 Dec 13 |
nicklas |
74 |
{ |
6372 |
06 Dec 13 |
nicklas |
75 |
continue; |
6372 |
06 Dec 13 |
nicklas |
76 |
} |
3438 |
04 Jun 07 |
nicklas |
77 |
if (!plugin.requiresConfiguration()) |
3438 |
04 Jun 07 |
nicklas |
78 |
{ |
3536 |
29 Jun 07 |
martin |
79 |
results.add(new ContextResult(sc, plugin, null, context, currentItem)); |
3438 |
04 Jun 07 |
nicklas |
80 |
} |
3534 |
28 Jun 07 |
martin |
81 |
if (configQuery != null && plugin.supportsConfigurations()) |
3438 |
04 Jun 07 |
nicklas |
82 |
{ |
3438 |
04 Jun 07 |
nicklas |
83 |
configQuery.setParameter("plugin", plugin.getId(), Type.INT); |
3438 |
04 Jun 07 |
nicklas |
84 |
List<PluginConfiguration> configs = configQuery.list(dc); |
3438 |
04 Jun 07 |
nicklas |
85 |
for (PluginConfiguration config : configs) |
3438 |
04 Jun 07 |
nicklas |
86 |
{ |
6372 |
06 Dec 13 |
nicklas |
// Skip config if user doesn't have USE Permission for it |
6372 |
06 Dec 13 |
nicklas |
88 |
if (!config.hasPermission(Permission.USE)) |
6372 |
06 Dec 13 |
nicklas |
89 |
{ |
6372 |
06 Dec 13 |
nicklas |
90 |
continue; |
6372 |
06 Dec 13 |
nicklas |
91 |
} |
3438 |
04 Jun 07 |
nicklas |
92 |
results.add(new ContextResult(sc, plugin, config, context, currentItem)); |
3438 |
04 Jun 07 |
nicklas |
93 |
} |
3438 |
04 Jun 07 |
nicklas |
94 |
} |
3438 |
04 Jun 07 |
nicklas |
95 |
} |
3438 |
04 Jun 07 |
nicklas |
96 |
return results; |
3438 |
04 Jun 07 |
nicklas |
97 |
} |
3438 |
04 Jun 07 |
nicklas |
98 |
|
3438 |
04 Jun 07 |
nicklas |
99 |
/** |
3438 |
04 Jun 07 |
nicklas |
Count all plug-in / configuration combinations that can be used. |
3438 |
04 Jun 07 |
nicklas |
@param results A collection of {@link ContextResult} objects |
3438 |
04 Jun 07 |
nicklas |
@return The number of objects in the collection where |
3438 |
04 Jun 07 |
nicklas |
{@link ContextResult#isInContext()} returns true |
3438 |
04 Jun 07 |
nicklas |
104 |
*/ |
3438 |
04 Jun 07 |
nicklas |
105 |
public static int countUsuablePlugins(Collection<ContextResult> results) |
3438 |
04 Jun 07 |
nicklas |
106 |
{ |
3438 |
04 Jun 07 |
nicklas |
107 |
int total = 0; |
3438 |
04 Jun 07 |
nicklas |
108 |
if (results != null) |
3438 |
04 Jun 07 |
nicklas |
109 |
{ |
3438 |
04 Jun 07 |
nicklas |
110 |
for (ContextResult result : results) |
3438 |
04 Jun 07 |
nicklas |
111 |
{ |
3438 |
04 Jun 07 |
nicklas |
112 |
if (result.isInContext()) ++total; |
3438 |
04 Jun 07 |
nicklas |
113 |
} |
3438 |
04 Jun 07 |
nicklas |
114 |
} |
3438 |
04 Jun 07 |
nicklas |
115 |
return total; |
3438 |
04 Jun 07 |
nicklas |
116 |
} |
3438 |
04 Jun 07 |
nicklas |
117 |
|
3438 |
04 Jun 07 |
nicklas |
118 |
/** |
3438 |
04 Jun 07 |
nicklas |
Get all messages from plug-in / configurations combinations that can't be used. |
3438 |
04 Jun 07 |
nicklas |
120 |
|
3438 |
04 Jun 07 |
nicklas |
@param results A collection of {@link ContextResult} objects |
3438 |
04 Jun 07 |
nicklas |
@param includeMessage If warning messages should be included or not |
3438 |
04 Jun 07 |
nicklas |
@param includeError If error messages should be include or not |
3438 |
04 Jun 07 |
nicklas |
@return A set containing the error / warning message |
3438 |
04 Jun 07 |
nicklas |
125 |
*/ |
3438 |
04 Jun 07 |
nicklas |
126 |
public static Set<String> getContextMessages(Collection<ContextResult> results, boolean includeMessage, boolean includeError) |
3438 |
04 Jun 07 |
nicklas |
127 |
{ |
3438 |
04 Jun 07 |
nicklas |
128 |
Set<String> messages = new HashSet<String>(); |
3438 |
04 Jun 07 |
nicklas |
129 |
if (results != null) |
3438 |
04 Jun 07 |
nicklas |
130 |
{ |
3438 |
04 Jun 07 |
nicklas |
131 |
for (ContextResult result : results) |
3438 |
04 Jun 07 |
nicklas |
132 |
{ |
3438 |
04 Jun 07 |
nicklas |
133 |
if (!result.isInContext() && (includeMessage || (includeError && result.isError()))) |
3438 |
04 Jun 07 |
nicklas |
134 |
{ |
3438 |
04 Jun 07 |
nicklas |
135 |
messages.add(result.getName() + ": " + result.getContextMessage()); |
3438 |
04 Jun 07 |
nicklas |
136 |
} |
3438 |
04 Jun 07 |
nicklas |
137 |
} |
3438 |
04 Jun 07 |
nicklas |
138 |
} |
3438 |
04 Jun 07 |
nicklas |
139 |
return messages; |
3438 |
04 Jun 07 |
nicklas |
140 |
} |
3438 |
04 Jun 07 |
nicklas |
141 |
|
3438 |
04 Jun 07 |
nicklas |
142 |
/** |
3438 |
04 Jun 07 |
nicklas |
Class for holding the result of a call to the |
3438 |
04 Jun 07 |
nicklas |
{@link InteractivePlugin#isInContext(GuiContext, Object)} |
3438 |
04 Jun 07 |
nicklas |
method. |
3438 |
04 Jun 07 |
nicklas |
146 |
*/ |
3438 |
04 Jun 07 |
nicklas |
147 |
public static class ContextResult |
3438 |
04 Jun 07 |
nicklas |
148 |
{ |
3438 |
04 Jun 07 |
nicklas |
149 |
private final PluginDefinition plugin; |
3438 |
04 Jun 07 |
nicklas |
150 |
private final PluginConfiguration config; |
3438 |
04 Jun 07 |
nicklas |
151 |
private String contextMessage; |
3438 |
04 Jun 07 |
nicklas |
152 |
private Throwable throwable; |
3438 |
04 Jun 07 |
nicklas |
153 |
|
3438 |
04 Jun 07 |
nicklas |
154 |
private ContextResult(SessionControl sc, PluginDefinition plugin, PluginConfiguration config, |
3438 |
04 Jun 07 |
nicklas |
155 |
GuiContext context, Object currentItem) |
3438 |
04 Jun 07 |
nicklas |
156 |
{ |
3438 |
04 Jun 07 |
nicklas |
157 |
this.plugin = plugin; |
3438 |
04 Jun 07 |
nicklas |
158 |
this.config = config; |
3438 |
04 Jun 07 |
nicklas |
159 |
InteractivePlugin ip = null; |
3438 |
04 Jun 07 |
nicklas |
160 |
try |
3438 |
04 Jun 07 |
nicklas |
161 |
{ |
5595 |
17 Mar 11 |
nicklas |
162 |
ip = plugin.newInstance(InteractivePlugin.class, sc, config, null); |
3438 |
04 Jun 07 |
nicklas |
163 |
contextMessage = ip.isInContext(context, currentItem); |
3438 |
04 Jun 07 |
nicklas |
164 |
} |
3438 |
04 Jun 07 |
nicklas |
165 |
catch (Throwable t) |
3438 |
04 Jun 07 |
nicklas |
166 |
{ |
3438 |
04 Jun 07 |
nicklas |
167 |
throwable = t; |
3438 |
04 Jun 07 |
nicklas |
168 |
contextMessage = t.getMessage() == null ? t.getClass().getName() : t.getMessage(); |
3438 |
04 Jun 07 |
nicklas |
169 |
} |
3438 |
04 Jun 07 |
nicklas |
170 |
finally |
3438 |
04 Jun 07 |
nicklas |
171 |
{ |
3438 |
04 Jun 07 |
nicklas |
172 |
if (ip != null) ip.done(); |
3438 |
04 Jun 07 |
nicklas |
173 |
} |
3438 |
04 Jun 07 |
nicklas |
174 |
} |
3438 |
04 Jun 07 |
nicklas |
175 |
|
3438 |
04 Jun 07 |
nicklas |
176 |
/** |
3438 |
04 Jun 07 |
nicklas |
Create a new warning-level result object. |
3438 |
04 Jun 07 |
nicklas |
@param plugin The plugin definition |
3438 |
04 Jun 07 |
nicklas |
@param config The plugin configuration or null |
3438 |
04 Jun 07 |
nicklas |
@param contextMessage The warning message, or null if there is no warning or error |
3438 |
04 Jun 07 |
nicklas |
181 |
*/ |
3438 |
04 Jun 07 |
nicklas |
182 |
public ContextResult(PluginDefinition plugin, PluginConfiguration config, String contextMessage) |
3438 |
04 Jun 07 |
nicklas |
183 |
{ |
3438 |
04 Jun 07 |
nicklas |
184 |
this.plugin = plugin; |
3438 |
04 Jun 07 |
nicklas |
185 |
this.config = config; |
3438 |
04 Jun 07 |
nicklas |
186 |
this.contextMessage = contextMessage; |
3438 |
04 Jun 07 |
nicklas |
187 |
} |
3438 |
04 Jun 07 |
nicklas |
188 |
|
3438 |
04 Jun 07 |
nicklas |
189 |
/** |
3438 |
04 Jun 07 |
nicklas |
Create a new error-level result object. |
3438 |
04 Jun 07 |
nicklas |
@param plugin The plugin definition |
3438 |
04 Jun 07 |
nicklas |
@param config The plugin configuration or null |
3438 |
04 Jun 07 |
nicklas |
@param t The exception that caused the error |
3438 |
04 Jun 07 |
nicklas |
194 |
*/ |
3438 |
04 Jun 07 |
nicklas |
195 |
public ContextResult(PluginDefinition plugin, PluginConfiguration config, Throwable t) |
3438 |
04 Jun 07 |
nicklas |
196 |
{ |
3438 |
04 Jun 07 |
nicklas |
197 |
this.plugin = plugin; |
3438 |
04 Jun 07 |
nicklas |
198 |
this.config = config; |
3438 |
04 Jun 07 |
nicklas |
199 |
this.throwable = t; |
3438 |
04 Jun 07 |
nicklas |
200 |
this.contextMessage = t.getMessage() == null ? t.getClass().getName() : t.getMessage(); |
3438 |
04 Jun 07 |
nicklas |
201 |
} |
3438 |
04 Jun 07 |
nicklas |
202 |
|
3438 |
04 Jun 07 |
nicklas |
203 |
/** |
3438 |
04 Jun 07 |
nicklas |
Get the plugin definition that was checked. |
3438 |
04 Jun 07 |
nicklas |
205 |
*/ |
3438 |
04 Jun 07 |
nicklas |
206 |
public PluginDefinition getPluginDefinition() |
3438 |
04 Jun 07 |
nicklas |
207 |
{ |
3438 |
04 Jun 07 |
nicklas |
208 |
return plugin; |
3438 |
04 Jun 07 |
nicklas |
209 |
} |
3438 |
04 Jun 07 |
nicklas |
210 |
|
3438 |
04 Jun 07 |
nicklas |
211 |
/** |
3438 |
04 Jun 07 |
nicklas |
Get the plugin configuration that was checked, or null. |
3438 |
04 Jun 07 |
nicklas |
213 |
*/ |
3438 |
04 Jun 07 |
nicklas |
214 |
public PluginConfiguration getPluginConfiguration() |
3438 |
04 Jun 07 |
nicklas |
215 |
{ |
3438 |
04 Jun 07 |
nicklas |
216 |
return config; |
3438 |
04 Jun 07 |
nicklas |
217 |
} |
3438 |
04 Jun 07 |
nicklas |
218 |
|
3438 |
04 Jun 07 |
nicklas |
219 |
/** |
3438 |
04 Jun 07 |
nicklas |
Get the warning level message, or the message from |
3438 |
04 Jun 07 |
nicklas |
{@link Throwable#getMessage()} if this is an error-level object. |
3438 |
04 Jun 07 |
nicklas |
222 |
*/ |
3438 |
04 Jun 07 |
nicklas |
223 |
public String getContextMessage() |
3438 |
04 Jun 07 |
nicklas |
224 |
{ |
3438 |
04 Jun 07 |
nicklas |
225 |
return contextMessage; |
3438 |
04 Jun 07 |
nicklas |
226 |
} |
3438 |
04 Jun 07 |
nicklas |
227 |
|
3438 |
04 Jun 07 |
nicklas |
228 |
/** |
3438 |
04 Jun 07 |
nicklas |
Get the exception that caused the error, or null if this is a |
3438 |
04 Jun 07 |
nicklas |
warning-level object. |
3438 |
04 Jun 07 |
nicklas |
231 |
*/ |
3438 |
04 Jun 07 |
nicklas |
232 |
public Throwable getError() |
3438 |
04 Jun 07 |
nicklas |
233 |
{ |
3438 |
04 Jun 07 |
nicklas |
234 |
return throwable; |
3438 |
04 Jun 07 |
nicklas |
235 |
} |
3438 |
04 Jun 07 |
nicklas |
236 |
|
3438 |
04 Jun 07 |
nicklas |
237 |
/** |
3438 |
04 Jun 07 |
nicklas |
Get the name of the plug-in configuration or, if the configuration is null, |
3438 |
04 Jun 07 |
nicklas |
of the plug-in definition |
3438 |
04 Jun 07 |
nicklas |
240 |
*/ |
3438 |
04 Jun 07 |
nicklas |
241 |
public String getName() |
3438 |
04 Jun 07 |
nicklas |
242 |
{ |
3438 |
04 Jun 07 |
nicklas |
243 |
return config != null ? config.getName() : plugin.getName(); |
3438 |
04 Jun 07 |
nicklas |
244 |
} |
3438 |
04 Jun 07 |
nicklas |
245 |
|
3438 |
04 Jun 07 |
nicklas |
246 |
/** |
3438 |
04 Jun 07 |
nicklas |
If the context check was successful and generate no warnings or error. |
3438 |
04 Jun 07 |
nicklas |
248 |
*/ |
3438 |
04 Jun 07 |
nicklas |
249 |
public boolean isInContext() |
3438 |
04 Jun 07 |
nicklas |
250 |
{ |
3438 |
04 Jun 07 |
nicklas |
251 |
return contextMessage == null && throwable == null; |
3438 |
04 Jun 07 |
nicklas |
252 |
} |
3438 |
04 Jun 07 |
nicklas |
253 |
|
3438 |
04 Jun 07 |
nicklas |
254 |
/** |
3438 |
04 Jun 07 |
nicklas |
If this is an error-level object. |
3438 |
04 Jun 07 |
nicklas |
256 |
*/ |
3438 |
04 Jun 07 |
nicklas |
257 |
public boolean isError() |
3438 |
04 Jun 07 |
nicklas |
258 |
{ |
3438 |
04 Jun 07 |
nicklas |
259 |
return throwable != null; |
3438 |
04 Jun 07 |
nicklas |
260 |
} |
3438 |
04 Jun 07 |
nicklas |
261 |
|
3438 |
04 Jun 07 |
nicklas |
262 |
} |
4594 |
21 Oct 08 |
nicklas |
263 |
|
4594 |
21 Oct 08 |
nicklas |
264 |
/** |
4594 |
21 Oct 08 |
nicklas |
Filter implementation that works on collections |
4594 |
21 Oct 08 |
nicklas |
of {@link ContextResult}:s and return only those |
4594 |
21 Oct 08 |
nicklas |
that return true from {@link ContextResult#isInContext()}. |
4594 |
21 Oct 08 |
nicklas |
268 |
|
4594 |
21 Oct 08 |
nicklas |
@author Nicklas |
4594 |
21 Oct 08 |
nicklas |
@version 2.9 |
4594 |
21 Oct 08 |
nicklas |
@base.modified $Date$ |
4594 |
21 Oct 08 |
nicklas |
272 |
*/ |
4594 |
21 Oct 08 |
nicklas |
273 |
public static class IsInContextFilter |
4594 |
21 Oct 08 |
nicklas |
274 |
implements Filter<ContextResult> |
4594 |
21 Oct 08 |
nicklas |
275 |
{ |
4594 |
21 Oct 08 |
nicklas |
276 |
|
4594 |
21 Oct 08 |
nicklas |
277 |
public IsInContextFilter() |
4594 |
21 Oct 08 |
nicklas |
278 |
{} |
4594 |
21 Oct 08 |
nicklas |
279 |
|
4594 |
21 Oct 08 |
nicklas |
280 |
/* |
4594 |
21 Oct 08 |
nicklas |
From the Filter interface |
4594 |
21 Oct 08 |
nicklas |
282 |
------------------------- |
4594 |
21 Oct 08 |
nicklas |
283 |
*/ |
4594 |
21 Oct 08 |
nicklas |
284 |
@Override |
4594 |
21 Oct 08 |
nicklas |
285 |
public boolean evaluate(ContextResult result) |
4594 |
21 Oct 08 |
nicklas |
286 |
{ |
4594 |
21 Oct 08 |
nicklas |
287 |
return result != null && result.isInContext(); |
4594 |
21 Oct 08 |
nicklas |
288 |
} |
4594 |
21 Oct 08 |
nicklas |
289 |
// ----------------------------- |
4594 |
21 Oct 08 |
nicklas |
290 |
} |
4594 |
21 Oct 08 |
nicklas |
291 |
|
3438 |
04 Jun 07 |
nicklas |
292 |
} |