1   package ixa.kaflib;
2   
3   import java.io.Serializable;
4   import java.util.*;
5   
6   
7   /** Dependencies represent dependency relations among terms. */
8   public class Dep implements Serializable {
9   
10      /** Source term of the dependency (required) */
11      private Term from;
12  
13      /** Target term of the dependency (required) */
14      private Term to;
15  
16      /** Relational function of the dependency (required) */
17      private String rfunc;
18  
19      /** Declension case (optional) */
20      private String depcase;
21  
22      Dep(Term from, Term to, String rfunc) {
23  	this.from = from;
24  	this.to = to;
25  	this.rfunc = rfunc;
26      }
27  
28      Dep(Dep dep, HashMap<String, Term> terms) {
29  	this.from = terms.get(dep.from.getId());
30  	if (this.from == null) {
31  	    throw new IllegalStateException("Couldn't find the term when loading dep (" + dep.getFrom().getId()+", "+dep.getTo().getId()+")");
32  	}
33  	this.to = terms.get(dep.to.getId());
34  	if (this.to == null) {
35  	    throw new IllegalStateException("Couldn't find the term when loading dep (" + dep.getFrom().getId()+", "+dep.getTo().getId()+")");
36  	}
37  	this.rfunc = dep.rfunc;
38  	this.depcase = dep.depcase;
39      }
40  
41      public Term getFrom() {
42  	return this.from;
43      }
44  
45      public void setFrom(Term term) {
46  	this.from = term;
47      }
48  
49      public Term getTo() {
50  	return to;
51      }
52  
53      public void setTo(Term term) {
54  	this.to = term;
55      }
56  
57      public String getRfunc() {
58  	return rfunc;
59      }
60  
61       public void setRfunc(String rfunc) {
62  	 this.rfunc = rfunc;
63      }
64  
65      public boolean hasCase() {
66  	return depcase != null;
67      }
68  
69      public String getCase() {
70  	return depcase;
71      }
72  
73      public void setCase(String depcase) {
74  	this.depcase = depcase;
75      }
76  
77      public String getStr() {
78          String idFrom = this.getFrom().getId().replaceAll("[^0-9]", "");
79          String idTo = this.getTo().getId().replaceAll("[^0-9]", "");
80          return String.format("%s(%s-%s, %s-%s)", rfunc, this.getFrom().getStr(), idFrom, this.getTo().getStr(), idTo);
81      }
82  
83      @Override
84      public String toString() {
85          return getStr();
86      }
87  
88      public static final class Path {
89  
90          private final List<Dep> deps;
91  
92          private final List<Term> terms;
93  
94          private String label;
95  
96          private Path(final List<Dep> deps, final List<Term> terms) {
97              this.deps = deps;
98              this.terms = terms;
99          }
100 
101         public static Path create(final Term from, final Term to, final Iterable<Dep> deps) {
102 
103             Term term = from;
104             List<Dep> depList = new ArrayList<>();
105             final List<Term> termList = new ArrayList<>();
106             termList.add(term);
107             for (final Dep dep : deps) {
108                 depList.add(dep);
109                 term = dep.getTo() == term ? dep.getFrom() : dep.getTo();
110                 if (!termList.add(term)) {
111                     throw new IllegalArgumentException("Path contains loop");
112                 }
113             }
114             if (!term.equals(to)) {
115                 throw new IllegalArgumentException("Invalid path");
116             }
117 
118             return new Path(depList, termList);
119         }
120 
121         public static Path create(final Term from, final Term to, final KAFDocument document) {
122 
123             // Handle empty path
124             if (from == to) {
125                 return create(from, to, Collections.<Dep>emptyList());
126             }
127 
128             // Determine path from dep root to TO node. End if the FROM node is found here
129             final List<Dep> toPath = new ArrayList<Dep>();
130             for (Dep dep = document.getDepToTerm(to); dep != null; dep = document.getDepToTerm(dep
131                     .getFrom())) {
132                 toPath.add(dep);
133                 if (dep.getFrom() == from) {
134                     Collections.reverse(toPath);
135                     return create(from, to, toPath);
136                 }
137             }
138 
139             // Determine path from dep root to FROM node. End if node in toPath is found here
140             final List<Dep> fromPath = new ArrayList<Dep>();
141             for (Dep dep = document.getDepToTerm(from); dep != null; dep = document
142                     .getDepToTerm(dep.getFrom())) {
143                 fromPath.add(dep);
144                 if (dep.getFrom() == to) {
145                     return create(from, to, fromPath);
146                 }
147                 for (int i = 0; i < toPath.size(); ++i) {
148                     if (dep.getFrom() == toPath.get(i).getFrom()) {
149                         for (int j = i; j >= 0; --j) {
150                             fromPath.add(toPath.get(j));
151                         }
152                         return create(from, to, fromPath);
153                     }
154                 }
155             }
156 
157             // No connection
158             return null;
159         }
160 
161         public Term getFrom() {
162             return this.terms.get(0);
163         }
164 
165         public Term getTo() {
166             return this.terms.get(this.terms.size() - 1);
167         }
168 
169         public List<Dep> getDeps() {
170             return this.deps;
171         }
172 
173         public List<Term> getTerms() {
174             return this.terms;
175         }
176 
177         public String getLabel() {
178             if (this.label == null) {
179                 final StringBuilder builder = new StringBuilder();
180                 Term term = this.terms.get(0);
181                 for (final Dep dep : this.deps) {
182                     builder.append(dep.getRfunc().toLowerCase());
183                     if (term.equals(dep.getFrom())) {
184                         builder.append('D');
185                         term = dep.getTo();
186                     } else {
187                         builder.append('U');
188                         term = dep.getFrom();
189                     }
190                 }
191                 this.label = builder.toString();
192             }
193             return this.label;
194         }
195 
196         public int length() {
197             return this.deps.size();
198         }
199 
200         public Path concat(final Path path) {
201             if (!path.getFrom().equals(getTo())) {
202                 throw new IllegalArgumentException();
203             }
204             if (this.deps.isEmpty()) {
205                 return path;
206             } else if (path.deps.isEmpty()) {
207                 return this;
208             } else {
209                 List<Dep> deps = new ArrayList<Dep>();
210                 deps.addAll(this.deps);
211                 deps.addAll(path.deps);
212                 return create(this.terms.get(0), path.terms.get(this.terms.size() - 1), deps);
213             }
214         }
215 
216         @Override
217         public boolean equals(final Object object) {
218             if (object == this) {
219                 return true;
220             }
221             if (!(object instanceof Path)) {
222                 return false;
223             }
224             final Path other = (Path) object;
225             return this.deps.equals(other.deps);
226         }
227 
228         @Override
229         public int hashCode() {
230             return Objects.hash(this.deps);
231         }
232 
233         @Override
234         public String toString() {
235             return getLabel();
236         }
237 
238     }
239     
240 }