source: trunk/src/org/expeditee/io/DefaultFrameWriter.java@ 1506

Last change on this file since 1506 was 1506, checked in by bnemhaus, 4 years ago

Permissions for encrypting frames are now respected. (Group level permissions still need testing and maybe implementation)

The current implementation of Hetrogeneous Owner requires that the owner of the frame specify the available labels. Injecting the property "HetrogeneousEncryptionLabels: <label name>" into the frame name item adds the specified label to the list of labels that those with 'Hetrogeneous (Owner)' permission are able to use.

File size: 19.5 KB
Line 
1/**
2 * DefaultFrameWriter.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.io;
20
21import java.io.BufferedOutputStream;
22import java.io.File;
23import java.io.FileOutputStream;
24import java.io.IOException;
25import java.io.OutputStream;
26import java.io.OutputStreamWriter;
27import java.io.Writer;
28import java.lang.reflect.Method;
29import java.util.Collection;
30import java.util.HashSet;
31import java.util.LinkedHashMap;
32import java.util.Set;
33
34import org.expeditee.agents.WriteTree;
35import org.expeditee.gio.EcosystemManager;
36import org.expeditee.gui.Frame;
37import org.expeditee.gui.FrameIO;
38import org.expeditee.items.Item;
39import org.expeditee.items.Line;
40import org.expeditee.items.Text;
41
42public abstract class DefaultFrameWriter extends ItemWriter implements
43 FrameWriter {
44 protected static final char MAGNETIZED_ITEM_BOTTOM = '/';
45 protected static final char MAGNETIZED_ITEM_TOP = '^';
46 protected static final char MAGNETIZED_ITEM_RIGHT = ']';
47 protected static final char MAGNETIZED_ITEM_LEFT = '[';
48 protected static final char PERMISSION = 'p';
49 protected static final char LINK_HISTORY = 'B';
50 protected static final char TOOLTIP = 'A';
51 protected static final char CONSTRAINT_IDS = 'c';
52 protected static final char LINE_IDS = 'l';
53 protected static final char THICKNESS = 'h';
54 protected static final char AUTO_WRAP_TO_SAVE = 'r';
55 protected static final char JUSTIFICATION = 'k';
56 protected static final char MIN_WIDTH_TO_SAVE = 'M';
57 protected static final char WIDTH_TO_SAVE = 'w';
58 protected static final char INITIAL_SPACING = 'm';
59 protected static final char LETTER_SPACING = 'b';
60 protected static final char WORD_SPACING = 'a';
61 protected static final char FORMULA = 'J';
62 protected static final char MASK = 'O';
63 protected static final char SPACING = 't';
64 protected static final char FONT = 'f';
65 protected static final char FILLED = 'z';
66 protected static final char DOT_TYPE = 'v';
67 protected static final char ARROW = 'j';
68 protected static final char LINE_PATTERN = 'g';
69 protected static final char LINK_TEMPLATE = 'y';
70 protected static final char LINK_FRAMESET = 'q';
71 protected static final char LINK_MARK = 'n';
72 protected static final char OWNER = 'o';
73 protected static final char FILL_PATTERN = 'i';
74 protected static final char ANCHOR_BOTTOM = 'I';
75 protected static final char ANCHOR_TOP = 'N';
76 protected static final char ANCHOR_RIGHT = 'H';
77 protected static final char ANCHOR_LEFT = 'R';
78 protected static final char GRADIENT_ANGLE = 'Q';
79 protected static final char GRADIENT_COLOR = 'E';
80 protected static final char FILL_COLOR = 'e';
81 protected static final char HIGHLIGHT = 'u';
82 protected static final char DATA = 'D';
83 protected static final char ACTION_LEAVE_FRAME = 'Y';
84 protected static final char ACTION_ENTER_FRAME = 'W';
85 protected static final char ACTION_CURSOR_LEAVE = 'V';
86 protected static final char ACTION_CURSOR_ENTER = 'U';
87 protected static final char ACTION_MARK = 'x';
88 protected static final char ACTION = 'X';
89 protected static final char LINK = 'F';
90 protected static final char TEXT = 'T';
91 protected static final char POSITION = 'P';
92 protected static final char BORDER_COLOR = 'K';
93 protected static final char BACKGROUND_COLOR = 'G';
94 protected static final char COLOR = 'd';
95 protected static final char DATE_CREATED = 's';
96 protected static final char TYPE_AND_ID = 'S';
97
98 public static final String TAB_INDEX_STR = "_ti";
99 public static final String SINGLE_LINE_ONLY_STR = "_sl";
100 public static final String PLACEHOLDER_STR = "_ph";
101 public static final String ENCRYPTION_LABEL_STR = "_el";
102 public static final String ACCEPTS_ENTER = "_ae";
103 public static final String MAGNETIZED_ITEM_BOTTOM_STR = MAGNETIZED_ITEM_BOTTOM + "";
104 public static final String MAGNETIZED_ITEM_TOP_STR = MAGNETIZED_ITEM_TOP + "";
105 public static final String MAGNETIZED_ITEM_RIGHT_STR = MAGNETIZED_ITEM_RIGHT + "";
106 public static final String MAGNETIZED_ITEM_LEFT_STR = MAGNETIZED_ITEM_LEFT + "";
107 public static final String PERMISSION_STR = PERMISSION + "";
108 public static final String LINK_HISTORY_STR = LINK_HISTORY + "";
109 public static final String TOOLTIP_STR = TOOLTIP + "";
110 public static final String CONSTRAINT_IDS_STR = CONSTRAINT_IDS + "";
111 public static final String LINE_IDS_STR = LINE_IDS + "";
112 public static final String THICKNESS_STR = THICKNESS + "";
113 public static final String AUTO_WRAP_TO_SAVE_STR = AUTO_WRAP_TO_SAVE + "";
114 public static final String JUSTIFICATION_STR = JUSTIFICATION + "";
115 public static final String MIN_WIDTH_TO_SAVE_STR = MIN_WIDTH_TO_SAVE + "";
116 public static final String WIDTH_TO_SAVE_STR = WIDTH_TO_SAVE + "";
117 public static final String INITIAL_SPACING_STR = INITIAL_SPACING + "";
118 public static final String LETTER_SPACING_STR = LETTER_SPACING + "";
119 public static final String WORD_SPACING_STR = WORD_SPACING + "";
120 public static final String FORMULA_STR = FORMULA + "";
121 public static final String MASK_STR = MASK + "";
122 public static final String SPACING_STR = SPACING + "";
123 public static final String FONT_STR = FONT + "";
124 public static final String FILLED_STR = FILLED + "";
125 public static final String DOT_TYPE_STR = DOT_TYPE + "";
126 public static final String ARROW_STR = ARROW + "";
127 public static final String LINE_PATTERN_STR = LINE_PATTERN + "";
128 public static final String LINK_TEMPLATE_STR = LINK_TEMPLATE + "";
129 public static final String LINK_FRAMESET_STR = LINK_FRAMESET + "";
130 public static final String LINK_MARK_STR = LINK_MARK + "";
131 public static final String OWNER_STR = OWNER + "";
132 public static final String FILL_PATTERN_STR = FILL_PATTERN + "";
133 public static final String ANCHOR_BOTTOM_STR = ANCHOR_BOTTOM + "";
134 public static final String ANCHOR_TOP_STR = ANCHOR_TOP + "";
135 public static final String ANCHOR_RIGHT_STR = ANCHOR_RIGHT + "";
136 public static final String ANCHOR_LEFT_STR = ANCHOR_LEFT + "";
137 public static final String GRADIENT_ANGLE_STR = GRADIENT_ANGLE + "";
138 public static final String GRADIENT_COLOR_STR = GRADIENT_COLOR + "";
139 public static final String FILL_COLOR_STR = FILL_COLOR + "";
140 public static final String HIGHLIGHT_STR = HIGHLIGHT + "";
141 public static final String DATA_STR = DATA + "";
142 public static final String ACTION_LEAVE_FRAME_STR = ACTION_LEAVE_FRAME + "";
143 public static final String ACTION_ENTER_FRAME_STR = ACTION_ENTER_FRAME + "";
144 public static final String ACTION_CURSOR_LEAVE_STR = ACTION_CURSOR_LEAVE + "";
145 public static final String ACTION_CURSOR_ENTER_STR = ACTION_CURSOR_ENTER + "";
146 public static final String ACTION_MARK_STR = ACTION_MARK + "";
147 public static final String ACTION_STR = ACTION + "";
148 public static final String LINK_STR = LINK + "";
149 public static final String TEXT_STR = TEXT + "";
150 public static final String POSITION_STR = POSITION + "";
151 public static final String BORDER_COLOR_STR = BORDER_COLOR + "";
152 public static final String BACKGROUND_COLOR_STR = BACKGROUND_COLOR + "";
153 public static final String COLOR_STR = COLOR + "";
154 public static final String DATE_CREATED_STR = DATE_CREATED + "";
155 public static final String TYPE_AND_ID_STR = TYPE_AND_ID + "";
156
157 protected String _filename = null;
158
159 protected String _output = null;
160
161 protected ProxyWriter _writer = null;
162
163 protected String _format = "";
164
165 protected boolean _running = true;
166
167 protected boolean _stop = false;
168
169 // keep track of methods that are put on the same line
170 private static LinkedHashMap<Character, Method> _ItemCharTags = new LinkedHashMap<Character, Method>();
171 // IMPORTANT: keys in _ItemTagsExt must start with underscore as their first character
172 private static LinkedHashMap<String, Method> _ItemStrTags = new LinkedHashMap<String, Method>();
173
174 private static LinkedHashMap<String, Method> _ItemTags = new LinkedHashMap<String, Method>();
175
176 protected static LinkedHashMap<Character, Method> _FrameTags = new LinkedHashMap<Character, Method>();
177
178 {
179 try {
180 /// C H I J L N P Q R S W X Y Z
181 /// a b c f g h i j l m n o q r s t u v w x y z
182 // Standard frame tags
183 _FrameTags.put('V', Frame.class.getMethod("getVersion"));
184 _FrameTags.put('p', Frame.class.getMethod("getPermission"));
185 _FrameTags.put('U', Frame.class.getMethod("getOwner"));
186 _FrameTags.put('D', Frame.class.getMethod("getDateCreated"));
187 _FrameTags.put('M', Frame.class.getMethod("getLastModifyUser"));
188 _FrameTags.put('d', Frame.class.getMethod("getLastModifyDate"));
189 _FrameTags.put('F', Frame.class.getMethod("getFrozenDate"));
190 _FrameTags.put('O', Frame.class.getMethod("getForegroundColor"));
191 _FrameTags.put('B', Frame.class.getMethod("getBackgroundColor"));
192 _FrameTags.put('T', Frame.class.getMethod("getData"));
193 _FrameTags.put('G', Frame.class.getMethod("getGroup"));
194
195 // Encryption frame tags
196 _FrameTags.put('K', Frame.class.getMethod("getFrameEncryptionLabel"));
197 _FrameTags.put('E', Frame.class.getMethod("getFrameEncryptionPermission"));
198 _FrameTags.put('k', Frame.class.getMethod("getHomogeneousEncryptionLabel"));
199 _FrameTags.put('e', Frame.class.getMethod("getEncryptionPermission"));
200 _FrameTags.put('a', Frame.class.getMethod("getHetrogeneousFrameOwnerLabels"));
201
202 // Note: As of 26/11/18 there are no unused letter item tags. Use other characters.
203 _ItemCharTags.put(DefaultFrameWriter.TYPE_AND_ID, Item.class.getMethod("getTypeAndID"));
204 _ItemCharTags.put(DefaultFrameWriter.DATE_CREATED, Item.class.getMethod("getDateCreated"));
205 _ItemCharTags.put(DefaultFrameWriter.COLOR, Item.class.getMethod("getColor"));
206 _ItemCharTags.put(DefaultFrameWriter.BACKGROUND_COLOR, Item.class.getMethod("getBackgroundColor"));
207 _ItemCharTags.put(DefaultFrameWriter.BORDER_COLOR, Item.class.getMethod("getBorderColor"));
208
209 _ItemCharTags.put(DefaultFrameWriter.POSITION, Item.class.getMethod("getPosition"));
210 _ItemCharTags.put(DefaultFrameWriter.TEXT, Text.class.getMethod("getText"));
211 _ItemCharTags.put(DefaultFrameWriter.LINK, Item.class.getMethod("getLink"));
212 _ItemCharTags.put(DefaultFrameWriter.ACTION, Item.class.getMethod("getAction"));
213 _ItemCharTags.put(DefaultFrameWriter.ACTION_MARK, Item.class.getMethod("getActionMark"));
214 _ItemCharTags.put(DefaultFrameWriter.ACTION_CURSOR_ENTER, Item.class.getMethod("getActionCursorEnter"));
215 _ItemCharTags.put(DefaultFrameWriter.ACTION_CURSOR_LEAVE, Item.class.getMethod("getActionCursorLeave"));
216 _ItemCharTags.put(DefaultFrameWriter.ACTION_ENTER_FRAME, Item.class.getMethod("getActionEnterFrame"));
217 _ItemCharTags.put(DefaultFrameWriter.ACTION_LEAVE_FRAME, Item.class.getMethod("getActionLeaveFrame"));
218 _ItemCharTags.put(DefaultFrameWriter.DATA, Item.class.getMethod("getData"));
219 _ItemCharTags.put(DefaultFrameWriter.HIGHLIGHT, Item.class.getMethod("getHighlight"));
220 _ItemCharTags.put(DefaultFrameWriter.FILL_COLOR, Item.class.getMethod("getFillColor"));
221 _ItemCharTags.put(DefaultFrameWriter.GRADIENT_COLOR, Item.class.getMethod("getGradientColor"));
222 _ItemCharTags.put(DefaultFrameWriter.GRADIENT_ANGLE, Item.class.getMethod("getGradientAngle"));
223
224 _ItemCharTags.put(DefaultFrameWriter.ANCHOR_LEFT, Item.class.getMethod("getAnchorLeft"));
225 _ItemCharTags.put(DefaultFrameWriter.ANCHOR_RIGHT, Item.class.getMethod("getAnchorRight"));
226 _ItemCharTags.put(DefaultFrameWriter.ANCHOR_TOP, Item.class.getMethod("getAnchorTop"));
227 _ItemCharTags.put(DefaultFrameWriter.ANCHOR_BOTTOM, Item.class.getMethod("getAnchorBottom"));
228
229 _ItemCharTags.put(DefaultFrameWriter.FILL_PATTERN, Item.class.getMethod("getFillPattern"));
230 _ItemCharTags.put(DefaultFrameWriter.OWNER, Item.class.getMethod("getOwner"));
231 _ItemCharTags.put(DefaultFrameWriter.LINK_MARK, Item.class.getMethod("getLinkMark"));
232 _ItemCharTags.put(DefaultFrameWriter.LINK_FRAMESET, Item.class.getMethod("getLinkFrameset"));
233 _ItemCharTags.put(DefaultFrameWriter.LINK_TEMPLATE, Item.class.getMethod("getLinkTemplate"));
234 _ItemCharTags.put(DefaultFrameWriter.LINE_PATTERN, Item.class.getMethod("getLinePattern"));
235
236 _ItemCharTags.put(DefaultFrameWriter.ARROW, Item.class.getMethod("getArrow"));
237
238 _ItemCharTags.put(DefaultFrameWriter.DOT_TYPE, Item.class.getMethod("getDotType"));
239 _ItemCharTags.put(DefaultFrameWriter.FILLED, Item.class.getMethod("getFilled"));
240
241 _ItemCharTags.put(DefaultFrameWriter.FONT, Text.class.getMethod("getFont"));
242 _ItemCharTags.put(DefaultFrameWriter.SPACING, Text.class.getMethod("getSpacing"));
243
244 _ItemCharTags.put(DefaultFrameWriter.MASK, Text.class.getMethod("getMask"));
245
246 // TODO set a boolean flag to indicate that the text is a formula
247 // Store the formula in the text property NOT the answer
248 _ItemCharTags.put(DefaultFrameWriter.FORMULA, Item.class.getMethod("getFormula"));
249
250 _ItemCharTags.put(DefaultFrameWriter.WORD_SPACING, Text.class.getMethod("getWordSpacing"));
251 _ItemCharTags.put(DefaultFrameWriter.LETTER_SPACING, Text.class.getMethod("getLetterSpacing"));
252 _ItemCharTags.put(DefaultFrameWriter.INITIAL_SPACING, Text.class.getMethod("getInitialSpacing"));
253 _ItemCharTags.put(DefaultFrameWriter.WIDTH_TO_SAVE, Text.class.getMethod("getWidthToSave"));
254 _ItemCharTags.put(DefaultFrameWriter.MIN_WIDTH_TO_SAVE, Text.class.getMethod("getMinWidthToSave"));
255 _ItemCharTags.put(DefaultFrameWriter.JUSTIFICATION, Text.class.getMethod("getJustification"));
256 _ItemCharTags.put(DefaultFrameWriter.AUTO_WRAP_TO_SAVE, Text.class.getMethod("getAutoWrapToSave"));
257
258 _ItemCharTags.put(DefaultFrameWriter.THICKNESS, Item.class.getMethod("getThickness"));
259 _ItemCharTags.put(DefaultFrameWriter.LINE_IDS, Item.class.getMethod("getLineIDs"));
260 _ItemCharTags.put(DefaultFrameWriter.CONSTRAINT_IDS, Item.class.getMethod("getConstraintIDs"));
261
262 _ItemCharTags.put(DefaultFrameWriter.TOOLTIP, Item.class.getMethod("getTooltip"));
263 _ItemCharTags.put(DefaultFrameWriter.LINK_HISTORY, Item.class.getMethod("getLinkHistory"));
264
265 _ItemCharTags.put(DefaultFrameWriter.PERMISSION, Item.class.getMethod("getPermission"));
266
267 _ItemCharTags.put(DefaultFrameWriter.MAGNETIZED_ITEM_LEFT, Item.class.getMethod("getMagnetizedItemLeft"));
268 _ItemCharTags.put(DefaultFrameWriter.MAGNETIZED_ITEM_RIGHT, Item.class.getMethod("getMagnetizedItemRight"));
269 _ItemCharTags.put(DefaultFrameWriter.MAGNETIZED_ITEM_TOP, Item.class.getMethod("getMagnetizedItemTop"));
270 _ItemCharTags.put(DefaultFrameWriter.MAGNETIZED_ITEM_BOTTOM, Item.class.getMethod("getMagnetizedItemBottom"));
271
272 _ItemStrTags.put(DefaultFrameWriter.ENCRYPTION_LABEL_STR, Item.class.getMethod("getEncryptionLabel"));
273
274 _ItemStrTags.put(DefaultFrameWriter.PLACEHOLDER_STR, Text.class.getMethod("getPlaceholder"));
275 _ItemStrTags.put(DefaultFrameWriter.SINGLE_LINE_ONLY_STR, Text.class.getMethod("isSingleLineOnly"));
276 _ItemStrTags.put(DefaultFrameWriter.TAB_INDEX_STR, Text.class.getMethod("getTabIndex"));
277 _ItemStrTags.put(DefaultFrameWriter.ACCEPTS_ENTER, Item.class.getMethod("acceptsKeyboardEnter"));
278 } catch (Exception e) {
279 e.printStackTrace();
280 }
281
282 Set<Character> keySet = _ItemCharTags.keySet();
283 for (Character key: keySet) {
284 _ItemTags.put(key + "", _ItemCharTags.get(key));
285 }
286 _ItemTags.putAll(_ItemStrTags);
287 }
288
289 public DefaultFrameWriter() {
290
291
292 }
293
294 public void setOutputLocation(String filename) {
295 _filename = filename;
296 }
297
298 public String writeFrame(Frame toWrite) throws IOException {
299 return writeFrame(toWrite, null);
300 }
301
302 public String writeFrame(Frame toWrite, Writer writer) throws IOException {
303 try {
304 initialise(toWrite, writer);
305
306 outputFrame(toWrite);
307
308 _running = false;
309 return finaliseFrame();
310 } catch (IOException ioe) {
311 _running = false;
312 throw ioe;
313 }
314
315 }
316
317 /**
318 * Called before writing out the body items of each frame.
319 *
320 * @param starting
321 * the name of the frame currently being written out.
322 * @throws IOException
323 */
324 protected void writeStartFrame(Frame starting) throws IOException {
325 if (starting.getTitleItem() != null) {
326 if (starting.getTitleItem().isAnnotation())
327 this.writeAnnotationTitle(starting.getTitleItem());
328 else
329 this.writeTitle(starting.getTitleItem(), starting.getSortedItems());
330 }
331 }
332
333 /**
334 * Called after writing out the body items of each frame.
335 *
336 * @param ending
337 * the name of the frame currently being written out.
338 * @throws IOException
339 */
340 protected void writeEndFrame(Frame ending) throws IOException {
341 }
342
343 protected final void initialise(Frame start) throws IOException {
344 initialise(start, null);
345 }
346
347 protected void initialise(Frame start, Writer writer) throws IOException {
348 if (_filename == null)
349 _filename = FrameIO.EXPORTS_PATH + getFileName(start) + _format;
350
351 if (writer != null) {
352 _writer = new ProxyWriter(writer);
353 _output = writer.toString();
354 } else if (_filename.equalsIgnoreCase(WriteTree.CLIPBOARD)) {
355 _writer = new ProxyWriter(true);
356 _output = WriteTree.CLIPBOARD;
357 } else {
358 if (_filename.contains(File.separator)) {
359 String extTest = _filename.substring(_filename
360 .lastIndexOf(File.separator) + 1);
361 if (!extTest.contains("."))
362 _filename += _format;
363 } else if (!_filename.contains("."))
364 _filename += _format;
365
366 if (!_filename.contains(File.separator))
367 _filename = FrameIO.EXPORTS_PATH + _filename;
368
369 File test = new File(_filename);
370
371 File parent = test.getParentFile();
372 if (parent != null && !parent.exists()) {
373 parent.mkdirs();
374 }
375
376 // Open an Output Stream Writer to set encoding
377 OutputStream fout = new FileOutputStream(_filename);
378 OutputStream bout = new BufferedOutputStream(fout);
379 Writer out = new OutputStreamWriter(bout, "UTF-8");
380
381 _writer = new ProxyWriter(out);
382 _output = _filename;
383 }
384 }
385
386 protected String getFileName(Frame start) {
387 return getValidFilename(start.getTitle());
388 }
389
390 public static String getValidFilename(String filename) {
391 return filename.replaceAll("[ \\.]", "_");
392 }
393
394 protected String finalise() throws IOException {
395 try {
396 _writer.flush();
397 _writer.close();
398 } catch (IOException ioe) {
399 } finally {
400 _writer.close();
401 }
402 String errorMessage = EcosystemManager.getMiscManager().open(_output);
403 if (errorMessage != null) System.err.println(errorMessage);
404 return " exported to " + _output;
405 }
406
407 protected String finaliseFrame() throws IOException {
408 return "Frame" + finalise();
409 }
410
411 protected void outputFrame(Frame toWrite) throws IOException {
412 writeStartFrame(toWrite);
413
414 Collection<Item> done = new HashSet<Item>();
415
416 for (Item i : getItemsToWrite(toWrite)) {
417 if (_stop) {
418 return;
419 }
420
421 if (i instanceof Line) {
422 if (done.contains(i)) {
423 continue;
424 }
425 done.addAll(i.getAllConnected());
426 }
427 writeItem(i);
428 }
429 writeEndFrame(toWrite);
430 }
431
432 protected Collection<Item> getItemsToWrite(Frame toWrite) {
433 return toWrite.getItemsToSave();
434 }
435
436 public boolean isRunning() {
437 return _running;
438 }
439
440 public void stop() {
441 _stop = true;
442 }
443
444 public String getFileContents() {
445 return "Not Supported";
446 }
447
448 public static LinkedHashMap<String, Method> getItemTags() {
449 return _ItemTags;
450 }
451
452 /**
453 * @return the _ItemTags
454 */
455 public static LinkedHashMap<Character, Method> getItemCharTags() {
456 return _ItemCharTags;
457 }
458
459 /**
460 * @return the _ItemTagsExt
461 */
462 public static LinkedHashMap<String, Method> getItemStrTags() {
463 return _ItemStrTags;
464 }
465
466}
Note: See TracBrowser for help on using the repository browser.