gitweb on Svarog
projekti pod git sistemom za održavanje verzija -- projects under
the git version control system
23ed58c0b0de246f762f95c6e3ed41b45b5c50f2
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
;
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
) {
25 public static class Config
{
27 public final String nullSymbol
;
28 public final int separation
;
29 public final int length
;
35 public Config(String nullSymbol
, int separation
, int length
) {
36 this.nullSymbol
= nullSymbol
;
37 this.separation
= separation
;
41 public Config
setNullSymbol(String nullSymbol
) {
42 return new Config(nullSymbol
, separation
, length
);
45 public Config
setSeparation(int separation
) {
46 return new Config(nullSymbol
, separation
, length
);
49 public Config
setLength(int length
) {
50 return new Config(nullSymbol
, separation
, length
);
54 protected Config config
;
56 public Config
getConfig() {
60 public void setConfig(Config config
) {
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());
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]);
82 protected void appendNode(StringBuilder builder
, Object tree
, Config config
, String prefix
) {
83 builder
.append(prefix
);
85 builder
.append(config
.nullSymbol
== null ? VERTICAL_SYMBOL
: config
.nullSymbol
);
87 builder
.append("(o)");
88 Object element
= getElement(tree
);
89 if (element
!= null) {
91 builder
.append(element
.toString());
97 protected void appendRight(StringBuilder builder
, Object tree
, Config config
, String
[] buildingBlocks
, boolean isRight
, String prefix
) {
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
) {
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
;
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();
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
);
187 if (matcher
.find()) {
188 level
= matcher
.start();
190 if (level
!= -1 && (config
.nullSymbol
== null || !line
.endsWith(config
.nullSymbol
))) {
191 Object tree
= parseTree(line
);
195 line
= in
.readLine();
197 Object tree
= formTree(0, elements
.size(), levels
, elements
);
201 protected Object
parseTree(String line
) {
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) {
210 value
= value
.substring(1);
213 throw new NumberFormatException(line
);
215 Object element
= null;
217 element
= newElement(value
);
219 Object tree
= newNode(element
);
223 protected Object
formTree(int beginIndex
, int endIndex
, List
<Integer
> levels
, List
<Object
> elements
) {
224 if (beginIndex
>= endIndex
) {
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
) {
236 Object tree
= elements
.get(minIndex
);
237 Object left
= formTree(minIndex
+ 1, endIndex
, levels
, elements
);
238 Object right
= formTree(beginIndex
, minIndex
, levels
, elements
);
240 setRight(tree
, right
);
248 @SuppressWarnings("unchecked")
249 public TreeIO(Class
<T
> type
, Config config
) {
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");
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) {
276 if (staticOne
== null) {
277 throw new IllegalArgumentException("Klasa " + this.treeType
.getName() + " nema staticku unutrasnju klasu koja predstavlja cvorove");
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");
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);
327 Field
[] declaredFields
= this.treeType
.getDeclaredFields();
328 Field rootField
= null;
329 for (Field field
: declaredFields
) {
330 if (Modifier
.isStatic(field
.getModifiers())) {
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");
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);
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())) {
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");
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");
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);
411 this.elementType
= elementField
.getType();
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);
463 Method elementFactoryMethod
= null;
464 Method
[] declaredMethods
= this.elementType
.getDeclaredMethods();
465 for (Method method
: declaredMethods
) {
466 if (!Modifier
.isStatic(method
.getModifiers())) {
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);
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);
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
) {
526 Object value
= rootField
.get(tree
);
528 } catch (IllegalAccessException e
) {
534 private void setRoot(T tree
, Object root
) {
536 rootField
.set(tree
, root
);
537 } catch (IllegalAccessException e
) {
542 private Object
getElement(Object node
) {
544 Object value
= elementField
.get(node
);
546 } catch (IllegalAccessException e
) {
552 private void setElement(Object node
, Object value
) {
554 elementField
.set(node
, value
);
555 } catch (IllegalAccessException e
) {
560 private Object
getLeft(Object node
) {
562 Object value
= leftField
.get(node
);
564 } catch (IllegalAccessException e
) {
570 private void setLeft(Object node
, Object value
) {
572 leftField
.set(node
, value
);
573 } catch (IllegalAccessException e
) {
578 private Object
getRight(Object node
) {
580 Object value
= rightField
.get(node
);
582 } catch (IllegalAccessException e
) {
588 private void setRight(Object node
, Object value
) {
590 rightField
.set(node
, value
);
591 } catch (IllegalAccessException e
) {
596 private Object
newElement(String value
) {
598 return elementFactoryMethod
.invoke(null, value
);
599 } catch (IllegalAccessException e
) {
602 } catch (InvocationTargetException e
) {
603 // Will not be a checked exception
604 RuntimeException cause
= (RuntimeException
) e
.getCause();
609 private Object
newNode(Object element
) {
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
);
620 } catch (IllegalAccessException e
) {
623 } catch (InstantiationException e
) {
626 } catch (InvocationTargetException e
) {
627 // Will not be a checked exception
628 RuntimeException cause
= (RuntimeException
) e
.getCause();
633 private T
newTree(Object root
) {
635 if (treeConstructor
!= null) {
636 return treeConstructor
.newInstance(root
);
638 T tree
= defaultTreeConstructor
.newInstance();
641 } catch (IllegalAccessException e
) {
644 } catch (InstantiationException e
) {
647 } catch (InvocationTargetException e
) {
648 // Will not be a checked exception
649 RuntimeException cause
= (RuntimeException
) e
.getCause();
Svarog.pmf.uns.ac.rs/gitweb
maintanance
Doni Pracner