gitweb on Svarog

projekti pod git sistemom za održavanje verzija -- projects under the git version control system
Stabla, primer konkretnog stabla osoba
[spa2-materijali.git] / Stabla / konkretnoStablo / TreeIO.java
2 import java.lang.reflect.Constructor;
3 import java.lang.reflect.Field;
4 import java.lang.reflect.InvocationTargetException;
5 import java.lang.reflect.Method;
6 import java.lang.reflect.Modifier;
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.regex.Matcher;
10 import java.util.regex.Pattern;
12 import org.svetovid.io.SvetovidReader;
13 import org.svetovid.io.SvetovidWriter;
15 public class TreeIO<T> {
17 public TreeIO(Class<T> type) {
18 this(type, null);
19 }
21 ////////////
22 // Config //
23 ////////////
25 public static class Config {
27 public final String nullSymbol;
28 public final int separation;
29 public final int length;
31 public Config() {
32 this(null, 1, 7);
33 }
35 public Config(String nullSymbol, int separation, int length) {
36 this.nullSymbol = nullSymbol;
37 this.separation = separation;
38 this.length = length;
39 }
41 public Config setNullSymbol(String nullSymbol) {
42 return new Config(nullSymbol, separation, length);
43 }
45 public Config setSeparation(int separation) {
46 return new Config(nullSymbol, separation, length);
47 }
49 public Config setLength(int length) {
50 return new Config(nullSymbol, separation, length);
51 }
52 }
54 protected Config config;
56 public Config getConfig() {
57 return config;
58 }
60 public void setConfig(Config config) {
61 this.config = config;
62 }
64 //////////////
65 // Printing //
66 //////////////
68 public void print(SvetovidWriter out, T tree) {
69 Object root = getRoot(tree);
70 StringBuilder builder = new StringBuilder();
71 appendTree(builder, root, config);
72 out.print(builder.toString());
73 }
75 protected void appendTree(StringBuilder builder, Object tree, Config config) {
76 String[] buildingBlocks = generateBuildingBlocks(config);
77 appendRight(builder, tree, config, buildingBlocks, true, buildingBlocks[5]);
78 appendNode(builder, tree, config, buildingBlocks[4]);
79 appendLeft(builder, tree, config, buildingBlocks, false, buildingBlocks[5]);
80 }
82 protected void appendNode(StringBuilder builder, Object tree, Config config, String prefix) {
83 builder.append(prefix);
84 if (tree == null) {
85 builder.append(config.nullSymbol == null ? VERTICAL_SYMBOL : config.nullSymbol);
86 } else {
87 builder.append("(o)");
88 Object element = getElement(tree);
89 if (element != null) {
90 builder.append(" ");
91 builder.append(element.toString());
92 }
93 }
94 builder.append("\n");
95 }
97 protected void appendRight(StringBuilder builder, Object tree, Config config, String[] buildingBlocks, boolean isRight, String prefix) {
98 if (tree == null) {
99 return;
101 Object subtree = getRight(tree);
102 if ((config.nullSymbol != null) || (subtree != null)) {
103 appendSubtree(builder, subtree, config, buildingBlocks, true, prefix);
104 for (int i = 0; i < config.separation; i++) {
105 appendEmpty(builder, buildingBlocks, prefix);
110 protected void appendLeft(StringBuilder builder, Object tree, Config config, String[] buildingBlocks, boolean isRight, String prefix) {
111 if (tree == null) {
112 return;
114 Object subtree = getLeft(tree);
115 if ((config.nullSymbol != null) || (subtree != null)) {
116 for (int i = 0; i < config.separation; i++) {
117 appendEmpty(builder, buildingBlocks, prefix);
119 appendSubtree(builder, subtree, config, buildingBlocks, false, prefix);
123 protected void appendEmpty(StringBuilder builder, String[] buildingBlocks, String prefix) {
124 builder.append(prefix);
125 builder.append(buildingBlocks[2]);
126 builder.append("\n");
129 protected void appendSubtree(StringBuilder builder, Object tree, Config config, String[] buildingBlocks, boolean isRight, String prefix) {
130 String myPrefix = prefix;
131 if (isRight == true) {
132 myPrefix = myPrefix + buildingBlocks[1];
134 if (isRight == false) {
135 myPrefix = myPrefix + buildingBlocks[3];
137 String noviPrefix = prefix + (!isRight ? buildingBlocks[2] : buildingBlocks[0]);
138 appendRight(builder, tree, config, buildingBlocks, isRight, noviPrefix);
139 appendNode(builder, tree, config, myPrefix);
140 noviPrefix = prefix + (isRight ? buildingBlocks[2] : buildingBlocks[0]);
141 appendLeft(builder, tree, config, buildingBlocks, isRight, noviPrefix);
144 protected static final String EMPTY_SYMBOL = " ";
145 protected static final String RIGHT_SYMBOL = "/";
146 protected static final String VERTICAL_SYMBOL = "|";
147 protected static final String LEFT_SYMBOL = "\\";
148 protected static final String HORIZONTAL_SYMBOL = "-";
150 protected String[] generateBuildingBlocks(Config config) {
151 String[] blocks = new String[6];
152 blocks[0] = generateBlock(EMPTY_SYMBOL, EMPTY_SYMBOL, EMPTY_SYMBOL, config.length - 2);
153 blocks[1] = generateBlock(EMPTY_SYMBOL, RIGHT_SYMBOL, HORIZONTAL_SYMBOL, config.length - 2);
154 blocks[2] = generateBlock(EMPTY_SYMBOL, VERTICAL_SYMBOL, EMPTY_SYMBOL, config.length - 2);
155 blocks[3] = generateBlock(EMPTY_SYMBOL, LEFT_SYMBOL, HORIZONTAL_SYMBOL, config.length - 2);
156 blocks[4] = HORIZONTAL_SYMBOL;
157 blocks[5] = EMPTY_SYMBOL;
158 return blocks;
161 protected String generateBlock(String emptySymbol, String startSymbol, String repeatSymbol, int repeatCount) {
162 StringBuilder builder = new StringBuilder();
163 builder.append(emptySymbol);
164 builder.append(startSymbol);
165 for (int i = 0; i < repeatCount; i++) {
166 builder.append(repeatSymbol);
168 return builder.toString();
171 /////////////
172 // Reading //
173 /////////////
175 public T read(SvetovidReader in) {
176 return newTree(parseTree(in, config));
179 protected Object parseTree(SvetovidReader in, Config config) {
180 List<Object> elements = new ArrayList<>();
181 List<Integer> levels = new ArrayList<>();
182 Pattern levelPattern = Pattern.compile("[\\Q" + LEFT_SYMBOL + HORIZONTAL_SYMBOL + RIGHT_SYMBOL + "\\E]");
183 String line = in.readLine();
184 while ((line != null) && !line.isEmpty()) {
185 Matcher matcher = levelPattern.matcher(line);
186 int level = -1;
187 if (matcher.find()) {
188 level = matcher.start();
190 if (level != -1 && (config.nullSymbol == null || !line.endsWith(config.nullSymbol))) {
191 Object tree = parseTree(line);
192 elements.add(tree);
193 levels.add(level);
195 line = in.readLine();
197 Object tree = formTree(0, elements.size(), levels, elements);
198 return tree;
201 protected Object parseTree(String line) {
202 String value;
203 int beginIndex = line.indexOf('(');
204 int endIndex = line.indexOf(')');
205 if ((beginIndex != -1) && (endIndex != -1) && (beginIndex < endIndex)) {
206 value = line.substring(endIndex + 1);
207 if (value.length() == 0) {
208 value = null;
209 } else {
210 value = value.substring(1);
212 } else {
213 throw new NumberFormatException(line);
215 Object element = null;
216 if (value != null) {
217 element = newElement(value);
219 Object tree = newNode(element);
220 return tree;
223 protected Object formTree(int beginIndex, int endIndex, List<Integer> levels, List<Object> elements) {
224 if (beginIndex >= endIndex) {
225 return null;
227 int minIndex = beginIndex;
228 int minLevel = levels.get(minIndex);
229 for (int i = beginIndex + 1; i < endIndex; i++) {
230 int level = levels.get(i);
231 if (level < minLevel) {
232 minLevel = level;
233 minIndex = i;
236 Object tree = elements.get(minIndex);
237 Object left = formTree(minIndex + 1, endIndex, levels, elements);
238 Object right = formTree(beginIndex, minIndex, levels, elements);
239 setLeft(tree, left);
240 setRight(tree, right);
241 return tree;
244 ///////////
245 // Magic //
246 ///////////
248 @SuppressWarnings("unchecked")
249 public TreeIO(Class<T> type, Config config) {
251 // Tree type
252 if (type == null) {
253 throw new IllegalArgumentException("Prosledjena klasa je null");
255 this.treeType = type;
256 if (Modifier.isAbstract(this.treeType.getModifiers())) {
257 throw new IllegalArgumentException("Klasa " + this.treeType.getName() + " ne sme da bude apstraktna");
260 // Node type
261 Class<?>[] declaredClasses = treeType.getDeclaredClasses();
262 if (declaredClasses.length == 0) {
263 throw new IllegalArgumentException("Klasa " + this.treeType.getName() + " nema unutrasnju klasu koja predstavlja cvorove");
265 Class<?> staticOne = null;
266 boolean multiStatic = false;
267 for (Class<?> cl: declaredClasses) {
268 if (Modifier.isStatic(cl.getModifiers())) {
269 if (staticOne == null) {
270 staticOne = cl;
271 } else {
272 multiStatic = true;
276 if (staticOne == null) {
277 throw new IllegalArgumentException("Klasa " + this.treeType.getName() + " nema staticku unutrasnju klasu koja predstavlja cvorove");
279 if (multiStatic) {
280 throw new IllegalArgumentException("Klasa " + this.treeType.getName() + " ima vise unutrasnjih statickih klasa, a mora biti samo jedna");
282 this.nodeType = staticOne;
283 if (Modifier.isAbstract(this.nodeType.getModifiers())) {
284 throw new IllegalArgumentException("Klasa " + this.nodeType.getName() + " ne sme da bude apstraktna");
286 if (!Modifier.isStatic(this.nodeType.getModifiers())) {
287 throw new IllegalArgumentException("Klasa " + this.nodeType.getName() + " mora da bude staticka");
290 // Tree constructors
291 Constructor<?>[] declaredConstructors = this.treeType.getDeclaredConstructors();
292 Constructor<T> defaultTreeConstructor = null;
293 Constructor<T> treeConstructor = null;
294 for (Constructor<?> constructor : declaredConstructors) {
295 boolean throwingExceptions = false;
296 for (Class<?> exception : constructor.getExceptionTypes()) {
297 if (!RuntimeException.class.isAssignableFrom(exception)) {
298 throwingExceptions = true;
301 Class<?>[] parameters = constructor.getParameterTypes();
302 if (parameters.length == 0
303 && !throwingExceptions) {
304 defaultTreeConstructor = (Constructor<T>) constructor;
306 if (parameters.length == 1
307 && parameters[0].isAssignableFrom(this.nodeType)
308 && !throwingExceptions) {
309 treeConstructor = (Constructor<T>) constructor;
312 if (defaultTreeConstructor == null && treeConstructor == null) {
313 throw new IllegalArgumentException("Klasa " + this.treeType.getName() + " nema "
314 + this.nodeType.getSimpleName() + "() ili "
315 + this.nodeType.getSimpleName() + "(" + this.nodeType.getName() + ") konstruktor");
317 this.defaultTreeConstructor = defaultTreeConstructor;
318 if (this.defaultTreeConstructor != null) {
319 this.defaultTreeConstructor.setAccessible(true);
321 this.treeConstructor = treeConstructor;
322 if (this.treeConstructor != null) {
323 this.treeConstructor.setAccessible(true);
326 // Tree root field
327 Field[] declaredFields = this.treeType.getDeclaredFields();
328 Field rootField = null;
329 for (Field field : declaredFields) {
330 if (Modifier.isStatic(field.getModifiers())) {
331 continue;
333 if (field.getType().isAssignableFrom(this.nodeType)) {
334 if (rootField != null) {
335 throw new IllegalArgumentException("Klasa " + this.treeType.getName() + " ima vise polja koji bi mogli predstavljati koren stabla");
337 rootField = field;
340 if (rootField == null) {
341 throw new IllegalArgumentException("Klasa " + this.treeType.getName() + " nema polje za predstavljanje korena");
343 this.rootField = rootField;
344 if (Modifier.isStatic(this.rootField.getModifiers())) {
345 throw new IllegalArgumentException("Polje " + this.treeType.getName() + "." + this.rootField.getName() + " ne sme da bude staticko");
347 this.rootField.setAccessible(true);
349 // Node fields
350 declaredFields = this.nodeType.getDeclaredFields();
351 if (declaredFields.length == 0) {
352 throw new IllegalArgumentException("Unutrasnja klasa " + this.nodeType.getName() + " nema deklarisanih polja");
354 Field elementField = null;
355 Field leftField = null;
356 Field rightField = null;
357 for (Field field : declaredFields) {
358 if (Modifier.isStatic(field.getModifiers())) {
359 continue;
361 String fieldName = field.getName();
362 if (fieldName.startsWith("e") || fieldName.startsWith("i") || fieldName.startsWith("o")) {
363 if (elementField != null) {
364 throw new IllegalArgumentException("Unutrasnja klasa " + this.nodeType.getName() + " ima vise polja za predstavljanje elementa");
366 elementField = field;
368 if (fieldName.startsWith("l")
369 && field.getType().isAssignableFrom(this.nodeType)) {
370 if (leftField != null) {
371 throw new IllegalArgumentException("Unutrasnja klasa " + this.nodeType.getName() + " ima vise polja za predstavljanje levog podstabla");
373 leftField = field;
375 if (fieldName.startsWith("d") || fieldName.startsWith("r")
376 && field.getType().isAssignableFrom(this.nodeType)) {
377 if (rightField != null) {
378 throw new IllegalArgumentException("Unutrasnja klasa " + this.nodeType.getName() + " ima vise polja za predstavljanje desnog podstabla");
380 rightField = field;
383 if (elementField == null) {
384 throw new IllegalArgumentException("Unutrasnja klasa " + this.nodeType.getName() + " nema polje za predstavljanje elementa");
386 if (leftField == null) {
387 throw new IllegalArgumentException("Unutrasnja klasa " + this.nodeType.getName() + " nema polje za predstavljanje levog podstabla");
389 if (rightField == null) {
390 throw new IllegalArgumentException("Unutrasnja klasa " + this.nodeType.getName() + " nema polje za predstavljanje desnog podstabla");
392 this.elementField = elementField;
393 if (Modifier.isStatic(this.elementField.getModifiers())) {
394 throw new IllegalArgumentException("Polje " + this.treeType.getName() + "." + this.elementField.getName() + " ne sme da bude staticko");
396 this.elementField.getModifiers();
398 this.elementField.setAccessible(true);
399 this.leftField = leftField;
400 if (Modifier.isStatic(this.leftField.getModifiers())) {
401 throw new IllegalArgumentException("Polje " + this.treeType.getName() + "." + this.leftField.getName() + " ne sme da bude staticko");
403 this.leftField.setAccessible(true);
404 this.rightField = rightField;
405 if (Modifier.isStatic(this.rightField.getModifiers())) {
406 throw new IllegalArgumentException("Polje " + this.treeType.getName() + "." + this.rightField.getName() + " ne sme da bude staticko");
408 this.rightField.setAccessible(true);
410 // Element type
411 this.elementType = elementField.getType();
413 // Node constructors
414 declaredConstructors = this.nodeType.getDeclaredConstructors();
415 Constructor<?> defaultNodeConstructor = null;
416 Constructor<?> nodeConstructor = null;
417 Constructor<?> nodeConstructor3 = null;
418 for (Constructor<?> constructor : declaredConstructors) {
419 boolean throwingExceptions = false;
420 for (Class<?> exception : constructor.getExceptionTypes()) {
421 if (!RuntimeException.class.isAssignableFrom(exception)) {
422 throwingExceptions = true;
425 Class<?>[] parameters = constructor.getParameterTypes();
426 if (parameters.length == 0
427 && !throwingExceptions) {
428 defaultNodeConstructor = constructor;
430 if (parameters.length == 1
431 && parameters[0].isAssignableFrom(this.elementType)
432 && !throwingExceptions) {
433 nodeConstructor = constructor;
435 if (parameters.length == 3
436 && parameters[0].isAssignableFrom(this.elementType)
437 && parameters[1].isAssignableFrom(this.nodeType)
438 && parameters[2].isAssignableFrom(this.nodeType)
439 && !throwingExceptions) {
440 nodeConstructor3 = constructor;
443 if (defaultNodeConstructor == null && nodeConstructor == null && nodeConstructor3 == null) {
444 throw new IllegalArgumentException("Unutrasnja klasa " + this.nodeType.getName() + " nema "
445 + this.nodeType.getSimpleName() + "() ili "
446 + this.nodeType.getSimpleName() + "(" + this.elementType.getName() + ") ili "
447 + this.nodeType.getSimpleName() + "(" + this.elementType.getName() + ", " + this.nodeType.getSimpleName() + ", " + this.nodeType.getSimpleName() + ") konstruktor");
449 this.defaultNodeConstructor = defaultNodeConstructor;
450 if (this.defaultNodeConstructor != null) {
451 this.defaultNodeConstructor.setAccessible(true);
453 this.nodeConstructor = nodeConstructor;
454 if (this.nodeConstructor != null) {
455 this.nodeConstructor.setAccessible(true);
457 this.nodeConstructor3 = nodeConstructor3;
458 if (this.nodeConstructor3 != null) {
459 this.nodeConstructor3.setAccessible(true);
462 // Element methods
463 Method elementFactoryMethod = null;
464 Method[] declaredMethods = this.elementType.getDeclaredMethods();
465 for (Method method : declaredMethods) {
466 if (!Modifier.isStatic(method.getModifiers())) {
467 continue;
469 boolean throwingExceptions = false;
470 for (Class<?> exception : method.getExceptionTypes()) {
471 if (!RuntimeException.class.isAssignableFrom(exception)) {
472 throwingExceptions = true;
475 String methodName = method.getName();
476 boolean familiarName = methodName.equals("fromString")
477 || methodName.equals("valueOf")
478 || methodName.equals("parse" + this.elementType.getSimpleName());
479 boolean goodParameters = method.getParameterTypes().length == 1 && (method.getParameterTypes()[0] == String.class || method.getParameterTypes()[0] == Object.class);
480 if (familiarName
481 && goodParameters
482 && this.elementType.isAssignableFrom(method.getReturnType())
483 && !throwingExceptions) {
484 if (elementFactoryMethod != null) {
485 throw new IllegalArgumentException("Klasa " + this.elementType.getName() + " ima vise "
486 + this.elementType.getSimpleName() + " fromString(String), "
487 + this.elementType.getSimpleName() + " valueOf(String) i "
488 + this.elementType.getSimpleName() + " parse" + this.elementType.getSimpleName() + "(String) metoda");
490 elementFactoryMethod = method;
493 if (elementFactoryMethod == null) {
494 throw new IllegalArgumentException("Klasa " + this.elementType.getName() + " nema "
495 + this.elementType.getSimpleName() + " fromString(String), "
496 + this.elementType.getSimpleName() + " valueOf(String) ili "
497 + this.elementType.getSimpleName() + " parse" + this.elementType.getSimpleName() + "(String) metod");
499 this.elementFactoryMethod = elementFactoryMethod;
500 this.elementFactoryMethod.setAccessible(true);
502 // Config
503 if (config == null) {
504 config = new Config();
506 this.config = config;
510 protected final Class<T> treeType;
511 protected final Constructor<T> treeConstructor;
512 protected final Constructor<T> defaultTreeConstructor;
513 protected final Field rootField;
514 protected final Class<?> nodeType;
515 protected final Field elementField;
516 protected final Field leftField;
517 protected final Field rightField;
518 protected final Constructor<?> nodeConstructor3;
519 protected final Constructor<?> nodeConstructor;
520 protected final Constructor<?> defaultNodeConstructor;
521 protected final Class<?> elementType;
522 protected final Method elementFactoryMethod;
524 private Object getRoot(T tree) {
525 try {
526 Object value = rootField.get(tree);
527 return value;
528 } catch (IllegalAccessException e) {
529 // Will never happen
530 return null;
534 private void setRoot(T tree, Object root) {
535 try {
536 rootField.set(tree, root);
537 } catch (IllegalAccessException e) {
538 // Will never happen
542 private Object getElement(Object node) {
543 try {
544 Object value = elementField.get(node);
545 return value;
546 } catch (IllegalAccessException e) {
547 // Will never happen
548 return null;
552 private void setElement(Object node, Object value) {
553 try {
554 elementField.set(node, value);
555 } catch (IllegalAccessException e) {
556 // Will never happen
560 private Object getLeft(Object node) {
561 try {
562 Object value = leftField.get(node);
563 return value;
564 } catch (IllegalAccessException e) {
565 // Will never happen
566 return null;
570 private void setLeft(Object node, Object value) {
571 try {
572 leftField.set(node, value);
573 } catch (IllegalAccessException e) {
574 // Will never happen
578 private Object getRight(Object node) {
579 try {
580 Object value = rightField.get(node);
581 return value;
582 } catch (IllegalAccessException e) {
583 // Will never happen
584 return null;
588 private void setRight(Object node, Object value) {
589 try {
590 rightField.set(node, value);
591 } catch (IllegalAccessException e) {
592 // Will never happen
596 private Object newElement(String value) {
597 try {
598 return elementFactoryMethod.invoke(null, value);
599 } catch (IllegalAccessException e) {
600 // Will never happen
601 return null;
602 } catch (InvocationTargetException e) {
603 // Will not be a checked exception
604 RuntimeException cause = (RuntimeException) e.getCause();
605 throw cause;
609 private Object newNode(Object element) {
610 try {
611 if (nodeConstructor != null) {
612 return nodeConstructor.newInstance(element);
614 if (nodeConstructor3 != null) {
615 return nodeConstructor3.newInstance(element, null, null);
617 Object node = defaultNodeConstructor.newInstance();
618 setElement(node, element);
619 return node;
620 } catch (IllegalAccessException e) {
621 // Will never happen
622 return null;
623 } catch (InstantiationException e) {
624 // Will never happen
625 return null;
626 } catch (InvocationTargetException e) {
627 // Will not be a checked exception
628 RuntimeException cause = (RuntimeException) e.getCause();
629 throw cause;
633 private T newTree(Object root) {
634 try {
635 if (treeConstructor != null) {
636 return treeConstructor.newInstance(root);
638 T tree = defaultTreeConstructor.newInstance();
639 setRoot(tree, root);
640 return tree;
641 } catch (IllegalAccessException e) {
642 // Will never happen
643 return null;
644 } catch (InstantiationException e) {
645 // Will never happen
646 return null;
647 } catch (InvocationTargetException e) {
648 // Will not be a checked exception
649 RuntimeException cause = (RuntimeException) e.getCause();
650 throw cause;
Svarog.pmf.uns.ac.rs/gitweb maintanance Doni Pracner