View Javadoc

1   /*
2    * This file is a part of CAST project.
3    * (c) Copyright 2007, AGH University of Science & Technology
4    * https://caribou.iisg.agh.edu.pl/trac/cast
5    *
6    * Licensed under the Eclipse Public License, Version 1.0 (the "License").
7    * You may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    * http://www.eclipse.org/legal/epl-v10.html
10   */
11  /*
12   * File: Link.java
13   * Created: 2007-09-19
14   * Author: apohllo
15   * $Id: Link.java 2361 2009-01-18 20:03:25Z awos $
16   */
17  
18  package pl.edu.agh.cast.model.mapper;
19  
20  import java.sql.PreparedStatement;
21  import java.sql.ResultSet;
22  import java.sql.SQLException;
23  import java.sql.Statement;
24  import java.sql.Types;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.Map;
30  
31  /**
32   * The link represents the raw connection between nodes in the base model.
33   *
34   * @author AGH CAST Team
35   */
36  public class Link {
37  
38      /**
39       *
40       */
41      private static final String SELECT_FIELDS = "SELECT id, src_id, dst_id, priority, name, " + //$NON-NLS-1$
42              "src_accessor, dst_accessor, src_position, dst_position "; //$NON-NLS-1$
43  
44      /**
45       *
46       */
47      private static final String DST_POSITION_FIELD = "dst_position"; //$NON-NLS-1$
48  
49      /**
50       *
51       */
52      private static final String SRC_POSITION_FIELD = "src_position"; //$NON-NLS-1$
53  
54      private Node source;
55  
56      private long sourceId;
57  
58      private Node destination;
59  
60      private long destinationId;
61  
62      private String name;
63  
64      private String sourceAccessor;
65  
66      private String destinationAccessor;
67  
68      private int priority;
69  
70      private long id;
71  
72      private int srcPosition = 0;
73  
74      private int dstPosition = 0;
75  
76      private boolean saved;
77  
78      private static Mapper mapper = Mapper.getInstance();
79  
80      private static Map<Long, Link> linkCache = Collections.synchronizedMap(new HashMap<Long, Link>());
81  
82      // ////////////////////////////////////////////////////////////////
83      // Constructors
84      // ////////////////////////////////////////////////////////////////
85  
86      /**
87       * Creates the link from key : value pairs.
88       *
89       * @param args
90       *            the map of key : value pairs.
91       */
92      public Link(Map args) {
93          JRubyIntegration.stringifyKeys(args);
94          int srcPos = 0;
95          if (args.containsKey(SRC_POSITION_FIELD)) {
96              srcPos = ((Long)args.get(SRC_POSITION_FIELD)).intValue();
97          }
98          int dstPos = 0;
99          if (args.containsKey(DST_POSITION_FIELD)) {
100             dstPos = ((Long)args.get(DST_POSITION_FIELD)).intValue();
101         }
102 
103         initialize((Node)args.get("src"), (Node)args.get("dst"), //$NON-NLS-1$ //$NON-NLS-2$
104                 (String)args.get("name"), translatePriority(args //$NON-NLS-1$
105                         .get("priority")), (String)args.get("src_accessor"), //$NON-NLS-1$ //$NON-NLS-2$
106                 (String)args.get("dst_accessor"), srcPos, dstPos); //$NON-NLS-1$
107     }
108 
109     /**
110      * Creates the link from given set of attributes.
111      *
112      * @param src
113      *            The source of the link
114      * @param dst
115      *            The target of the link
116      * @param name
117      *            The name of the link
118      * @param priority
119      *            The priority of the link
120      */
121     public Link(Node src, Node dst, String name, int priority) {
122         initialize(src, dst, name, priority, null, null, 0, 0);
123     }
124 
125     /**
126      * Lazy binding constructor.
127      *
128      * @param srcId
129      *            The id of the source node
130      * @param dstId
131      *            The id of the destination node
132      * @param name
133      *            The name of the link
134      * @param priority
135      *            The priority of the link
136      */
137     public Link(long srcId, long dstId, String name, int priority) {
138         sourceId = srcId;
139         destinationId = dstId;
140         initialize(null, null, name, priority, null, null, 0, 0);
141     }
142 
143     public Link(Node src, Node dst, String name, Priority priority, String srcAccessor, String dstAccessor) {
144         initialize(src, dst, name, priority.ordinal(), srcAccessor, dstAccessor, 0, 0);
145     }
146 
147     public Link(Node src, Node dst, String name, Priority priority) {
148         initialize(src, dst, name, priority.ordinal(), null, null, 0, 0);
149     }
150 
151     public Link(Node src, Node dst, String name, Priority priority, String srcAcc, String dstAcc, int srcPosition,
152             int dstPosition) {
153         initialize(src, dst, name, priority.ordinal(), srcAcc, dstAcc, srcPosition, dstPosition);
154     }
155 
156     private void initialize(Node src, Node dst, String newName, int newPriority, String newSrcAccessor,
157             String newDstAccessor, int newSrcPosition, int newDstPosition) {
158         setSrc(src);
159         if (src != null) {
160             src.addSrcLink(this);
161         }
162         setDst(dst);
163         if (dst != null) {
164             dst.addDstLink(this);
165         }
166         name = newName;
167         priority = newPriority;
168         sourceAccessor = newSrcAccessor;
169         destinationAccessor = newDstAccessor;
170         srcPosition = newSrcPosition;
171         dstPosition = newDstPosition;
172     }
173 
174     private static PreparedStatement findWithDstStmt;
175 
176     private static PreparedStatement findWithSrcStmt;
177 
178     private static PreparedStatement findStmt;
179 
180     private static PreparedStatement saveStmt;
181 
182     static {
183         String stmtStr;
184         try {
185             stmtStr = SELECT_FIELDS + "FROM links WHERE dst_id = ? ORDER BY src_position"; //$NON-NLS-1$
186             findWithDstStmt = mapper.getConnection().prepareStatement(stmtStr);
187 
188             stmtStr = SELECT_FIELDS + " FROM links WHERE src_id = ? ORDER BY dst_position"; //$NON-NLS-1$
189             findWithSrcStmt = mapper.getConnection().prepareStatement(stmtStr);
190 
191             stmtStr = SELECT_FIELDS + " FROM links WHERE id = ?"; //$NON-NLS-1$
192             findStmt = mapper.getConnection().prepareStatement(stmtStr);
193 
194             stmtStr = "INSERT INTO links SET name = ?, src_id = ?, " //$NON-NLS-1$
195                     + "dst_id = ?, priority = ?, src_accessor = ?, " //$NON-NLS-1$
196                     + "dst_accessor = ?, src_position = ?, dst_position = ? "; //$NON-NLS-1$
197             saveStmt = mapper.getConnection().prepareStatement(stmtStr, Statement.RETURN_GENERATED_KEYS);
198         } catch (SQLException e) {
199             e.printStackTrace();
200         }
201     }
202 
203     /**
204      * Save given metamodel link in the DB.
205      *
206      * <p>
207      * The saving process is recursive. Source and destination nodes are saved first and then the link is saved.
208      * </p>
209      *
210      * @return True if the save was successful (or link has been saved earlier).
211      */
212     public boolean save() {
213         if (saved) {
214             return true;
215         }
216 
217         // System.out.println("Save link")
218 
219         // validation
220         if (name == null || name.equals("")) { //$NON-NLS-1$
221             throw new RuntimeException("Link's name must not be blank: " + this); //$NON-NLS-1$
222         }
223 
224         if (getSrc() == null) {
225             throw new RuntimeException("Link's src must not be null: " + this); //$NON-NLS-1$
226         }
227 
228         if (getDst() == null) {
229             throw new RuntimeException("Link's dst must not be null: " + this); //$NON-NLS-1$
230         }
231 
232         try {
233             if (id == 0) {
234                 // new node
235 
236                 saveStmt.setString(1, name);
237 
238                 setSaved(true);
239                 long srcId = getSrc().getId();
240                 if (srcId == 0) {
241                     if (!getSrc().save()) {
242                         setSaved(false);
243                         return false;
244                     }
245                     srcId = getSrc().getId();
246                     if (srcId == 0) {
247                         throw new RuntimeException("Node id is 0: " + getSrc()); //$NON-NLS-1$
248                     }
249 
250                 }
251                 saveStmt.setLong(2, srcId);
252 
253                 long dstId = getDst().getId();
254                 if (dstId == 0) {
255                     if (!getDst().save()) {
256                         setSaved(false);
257                         return false;
258                     }
259                     dstId = getDst().getId();
260                     if (dstId == 0) {
261                         throw new RuntimeException("Node id is 0: " + getDst()); //$NON-NLS-1$
262                     }
263 
264                 }
265                 saveStmt.setLong(3, dstId);
266 
267                 saveStmt.setInt(4, getPriority());
268 
269                 String srcAccessor = getSrcAccessor();
270                 if (srcAccessor != null) {
271                     saveStmt.setString(5, srcAccessor);
272                 } else {
273                     saveStmt.setNull(5, Types.VARCHAR);
274                 }
275 
276                 String dstAccessor = getDstAccessor();
277                 if (dstAccessor != null) {
278                     saveStmt.setString(6, dstAccessor);
279                 } else {
280                     saveStmt.setNull(6, Types.VARCHAR);
281                 }
282 
283                 saveStmt.setInt(7, getSrcPosition());
284                 saveStmt.setInt(8, getDstPosition());
285 
286                 saveStmt.executeUpdate();
287                 ResultSet rs = saveStmt.getGeneratedKeys();
288                 if (rs.next()) {
289                     setId(rs.getLong(1));
290                     setSaved(true);
291                 } else {
292                     throw new SQLException("Cannot obtain the last link id"); //$NON-NLS-1$
293                 }
294                 return true;
295             }
296         } catch (SQLException e) {
297             System.err.println(e.getMessage());
298             setSaved(false);
299             return false;
300         }
301 
302         return false;
303     }
304 
305     // ////////////////////////////////////////
306     // priority
307     // ////////////////////////////////////////
308     /**
309      * Priority reader.
310      *
311      * @return Priority value
312      */
313     public int getPriority() {
314         return priority;
315     }
316 
317     /**
318      * Priority setter.
319      *
320      * @param newPriority
321      *            priority value
322      */
323     public void setPriority(int newPriority) {
324         priority = newPriority;
325     }
326 
327     /**
328      * Priority setter (tries to translate given object to priority).
329      *
330      * @param priority
331      *            priority value
332      */
333     public void setPriority(Object priority) {
334         setPriority(translatePriority(priority));
335     }
336 
337     private int translatePriority(Object priorityObj) {
338         int priorityValue = 0;
339         if (priorityObj != null) {
340             if (priorityObj.toString().equals("src_over_dst")) {
341                 priorityValue = 1;
342             } else if (priorityObj.toString().equals("dst_over_src")) {
343                 priorityValue = 2;
344             }
345         }
346         return priorityValue;
347     }
348 
349     // //////////////////////////////////////////////////
350     // Source and destination
351     // //////////////////////////////////////////////////
352     /**
353      * Destination node getter. Due to DB load optimization, must be used internally as well.
354      *
355      * @return Destination metamodel node
356      */
357     public Node getDst() {
358         if (destination == null && destinationId != 0) {
359             destination = Node.find(destinationId);
360         }
361         return destination;
362     }
363 
364     /**
365      * The destination id getter.
366      *
367      * @return The destination id.
368      */
369     public long getDstId() {
370         if (destinationId == 0 && destination != null) {
371             destinationId = destination.getId();
372         }
373         return destinationId;
374     }
375 
376     /**
377      * Source node getter. Due to DB load optimization, must be used internally as well.
378      *
379      * @return Source metamodel node
380      */
381     public Node getSrc() {
382         if (source == null && sourceId != 0) {
383             source = Node.find(sourceId);
384         }
385         return source;
386     }
387 
388     /**
389      * The source id getter.
390      *
391      * @return The source id.
392      */
393     public long getSrcId() {
394         if (sourceId == 0 && source != null) {
395             sourceId = source.getId();
396         }
397         return sourceId;
398     }
399 
400     /**
401      * DB indentifier reader.
402      *
403      * @return DB identifier
404      */
405     public long getId() {
406         return id;
407     }
408 
409     /**
410      *
411      * {@inheritDoc}
412      *
413      * @see java.lang.Object#toString()
414      */
415     @Override
416     public String toString() {
417         StringBuilder builder = new StringBuilder();
418         builder.append("Link: "); //$NON-NLS-1$
419         builder.append(name);
420         builder.append(" ["); //$NON-NLS-1$
421         builder.append(id);
422         builder.append(", "); //$NON-NLS-1$
423         builder.append(priority);
424         builder.append("] src "); //$NON-NLS-1$
425         builder.append(getSrcId());
426         builder.append(" ["); //$NON-NLS-1$
427         builder.append(getSrcPosition());
428         builder.append("] dst "); //$NON-NLS-1$
429         builder.append(getDstId());
430         builder.append(" ["); //$NON-NLS-1$
431         builder.append(getDstPosition());
432         builder.append("]"); //$NON-NLS-1$
433         return builder.toString();
434     }
435 
436     /**
437      * Name getter.
438      *
439      * @return Name of the link
440      */
441     public String getName() {
442         return name;
443     }
444 
445     /**
446      * Source setter.
447      *
448      * @param srcNode
449      *            Node to be set as source of the link
450      */
451     public void setSrc(Node srcNode) {
452         source = srcNode;
453         if (source != null) {
454             sourceId = source.getId();
455         }
456     }
457 
458     /**
459      * Destination setter.
460      *
461      * @param dst
462      *            Node to be set as desitintion of the link
463      */
464     public void setDst(Node dst) {
465         destination = dst;
466         if (destination != null) {
467             destinationId = destination.getId();
468         }
469     }
470 
471     /**
472      *
473      * {@inheritDoc}
474      *
475      * @see java.lang.Object#equals(java.lang.Object)
476      */
477     @Override
478     public boolean equals(Object other) {
479         if (this == other) {
480             return true;
481         }
482 
483         if (!(other instanceof Link)) {
484             return false;
485         }
486 
487         Link that = (Link)other;
488 
489         // srcAccessor may be null
490         if (this.sourceAccessor == null) {
491             if (that.sourceAccessor != null) {
492                 return false;
493             }
494         } else {
495             if (!this.sourceAccessor.equals(that.sourceAccessor)) {
496                 return false;
497             }
498         }
499 
500         // dstAccessor may be null
501         if (this.destinationAccessor == null) {
502             if (that.destinationAccessor != null) {
503                 return false;
504             }
505         } else {
506             if (!this.destinationAccessor.equals(that.destinationAccessor)) {
507                 return false;
508             }
509         }
510 
511         return this.id == that.id && this.getDstId() == that.getDstId() && this.getSrcId() == that.getSrcId()
512                 && this.name.equals(that.name) && this.priority == that.priority
513                 && this.srcPosition == that.srcPosition && this.dstPosition == that.dstPosition;
514     }
515 
516     /**
517      * Find link with given id of null if not present.
518      *
519      * @param linkId
520      *            The id of the link
521      * @return Link with given id or null
522      */
523     public static Link find(long linkId) {
524         // System.out.println("Find link "+linkId);
525         Link link;
526 
527         // try cache first
528         link = linkCache.get(linkId);
529         if (link != null) {
530             return link;
531         }
532 
533         try {
534             findStmt.setLong(1, linkId);
535             ResultSet rs = findStmt.executeQuery();
536 
537             if (rs.next()) {
538                 link = createLink(rs, 0);
539             } else {
540                 return null;
541             }
542             return link;
543 
544         } catch (SQLException e) {
545             e.printStackTrace();
546         } finally {
547             try {
548                 findStmt.close();
549             } catch (SQLException e) {
550                 e.printStackTrace();
551             }
552         }
553 
554         return null;
555     }
556 
557     /**
558      * Creates link based on the data found in the DB. The link cache is consulted first to ensure referential integrity
559      * (i.e. link with the same id are created only once). TODO cache invalidation (needed?)
560      *
561      * @param resultSet
562      *            The data to be used to create the link
563      * @return New link initialized with the data stored in the DB.
564      * @throws SQLException
565      */
566     static Link createLink(ResultSet resultSet, int fieldOffset) throws SQLException {
567         long linkId = resultSet.getLong(fieldOffset + 1);
568         Link link;
569 
570         // try link cache first
571         link = linkCache.get(linkId);
572         if (link != null) {
573             return link;
574         }
575 
576         link = new Link(resultSet.getLong(fieldOffset + 2), resultSet.getLong(fieldOffset + 3), resultSet
577                 .getString(fieldOffset + 5), resultSet.getInt(fieldOffset + 4));
578         link.setId(linkId);
579         link.setSaved(true);
580         link.setSrcAccessor(resultSet.getString(fieldOffset + 6));
581         link.setDstAccessor(resultSet.getString(fieldOffset + 7));
582         link.setSrcPosition(resultSet.getInt(fieldOffset + 8));
583         link.setDstPosition(resultSet.getInt(fieldOffset + 9));
584         linkCache.put(linkId, link);
585         return link;
586     }
587 
588     public void setSrcPosition(int position) {
589         srcPosition = position;
590     }
591 
592     /**
593      * The position of source node among other nodes at the source side of the relationship (set of links).
594      *
595      * @return The position of the source node.
596      */
597     public int getSrcPosition() {
598         return srcPosition;
599     }
600 
601     /**
602      * The target position setter.
603      *
604      * @param position
605      *            The position to set.
606      */
607     public void setDstPosition(int position) {
608         dstPosition = position;
609     }
610 
611     /**
612      * The position of destination node among other nodes at the destination side of the relationship (set of links).
613      *
614      * @return The position of the target node.
615      */
616     public int getDstPosition() {
617         return dstPosition;
618     }
619 
620     /**
621      * The hash code.
622      *
623      * @return hash code
624      */
625     @Override
626     public int hashCode() {
627         int value = (int)(name.hashCode() ^ priority + (id * 7) ^ (getSrcId() * 17) ^ (getDstId() * 19)
628                 ^ (srcPosition * 11) ^ (dstPosition * 23));
629         if (sourceAccessor != null) {
630             value ^= sourceAccessor.hashCode();
631         } else {
632             value += 100000;
633         }
634         if (destinationAccessor != null) {
635             value ^= destinationAccessor.hashCode();
636         } else {
637             value += 200;
638         }
639 
640         return value;
641     }
642 
643     /**
644      * Returns the name of the source node accessor method.
645      *
646      * @return Source node getter
647      */
648     public String getSrcAccessor() {
649         return sourceAccessor;
650     }
651 
652     /**
653      * Returns the name of the destination node accessor method.
654      *
655      * @return Destination node getter
656      */
657     public String getDstAccessor() {
658         return destinationAccessor;
659     }
660 
661     /**
662      * Saved flag setter.
663      *
664      * @param newSaved
665      *            New saved value.
666      */
667     public void setSaved(boolean newSaved) {
668         saved = newSaved;
669     }
670 
671     /**
672      * Id setter.
673      *
674      * @param newId
675      *            New id
676      */
677     public void setId(long newId) {
678         id = newId;
679     }
680 
681     /**
682      * Source accessor setter.
683      *
684      * @param accessor
685      *            The source accessor.
686      */
687     public void setSrcAccessor(String accessor) {
688         sourceAccessor = accessor;
689     }
690 
691     /**
692      * Target accessor setter.
693      *
694      * @param accessor
695      *            The target accessor.
696      */
697     public void setDstAccessor(String accessor) {
698         destinationAccessor = accessor;
699     }
700 
701     /**
702      * Find link which is equal to given link.
703      *
704      * @param link
705      *            The link which is used as the query example.
706      * @return The link with the same id.
707      */
708     public static Link find(Link link) {
709         return find(link.getId());
710     }
711 
712     /**
713      * Find all link whose source is a node with given id.
714      *
715      * @param nodeId
716      *            The id of the source node
717      * @return List of links
718      * @throws SQLException
719      */
720     public static List<Link> findWithSrc(long nodeId) throws SQLException {
721 
722         findWithSrcStmt.setLong(1, nodeId);
723         ResultSet rs = findWithSrcStmt.executeQuery();
724         List<Link> result = new LinkedList<Link>();
725         while (rs.next()) {
726             result.add(createLink(rs, 0));
727         }
728         return result;
729     }
730 
731     /**
732      * Find all link whose destination is a node with given id.
733      *
734      * @param nodeId
735      *            The id of the destination node
736      * @return List of links
737      * @throws SQLException
738      */
739     public static List<Link> findWithDst(long nodeId) throws SQLException {
740         findWithDstStmt.setLong(1, nodeId);
741         ResultSet rs = findWithDstStmt.executeQuery();
742         List<Link> result = new LinkedList<Link>();
743         while (rs.next()) {
744             result.add(createLink(rs, 0));
745         }
746         return result;
747     }
748 
749     /**
750      * Print out the stats of the link cache.
751      */
752     public static void stats() {
753         // TODO DO NOT USE PRINTLN
754         // System.out.println("Link cache " + _linkCache.size());
755     }
756 
757     static void clearCache() {
758         linkCache.clear();
759     }
760 
761 }