001package de.deepamehta.topicmaps.model;
002
003import de.deepamehta.core.JSONEnabled;
004import de.deepamehta.core.model.TopicModel;
005import de.deepamehta.core.model.topicmaps.AssociationViewModel;
006import de.deepamehta.core.model.topicmaps.TopicViewModel;
007import de.deepamehta.core.util.DeepaMehtaUtils;
008
009import org.codehaus.jettison.json.JSONObject;
010
011import java.awt.Point;
012
013import java.util.Map;
014import java.util.logging.Logger;
015
016
017
018/**
019 * A topicmap viewmodel: a collection of topics and associations plus their view properties.
020 * <p>
021 * Features:
022 * - Serialization to JSON.
023 */
024public class TopicmapViewmodel implements JSONEnabled {
025
026    // ---------------------------------------------------------------------------------------------- Instance Variables
027
028    private TopicModel topicmapTopic;
029    private Map<Long, TopicViewModel> topics;
030    private Map<Long, AssociationViewModel> assocs;
031
032    private Logger logger = Logger.getLogger(getClass().getName());
033
034    // ---------------------------------------------------------------------------------------------------- Constructors
035
036    public TopicmapViewmodel(TopicModel topicmapTopic, Map<Long, TopicViewModel> topics,
037                                                       Map<Long, AssociationViewModel> assocs) {
038        this.topicmapTopic = topicmapTopic;
039        this.topics = topics;
040        this.assocs = assocs;
041    }
042
043    // -------------------------------------------------------------------------------------------------- Public Methods
044
045    public long getId() {
046        return topicmapTopic.getId();
047    }
048
049    // ---
050
051    public Iterable<TopicViewModel> getTopics() {
052        return topics.values();
053    }
054
055    public Iterable<AssociationViewModel> getAssociations() {
056        return assocs.values();
057    }
058
059    // ---
060
061    public TopicViewModel getTopic(long id) {
062        return topics.get(id);
063    }
064
065    public AssociationViewModel getAssociation(long id) {
066        return assocs.get(id);
067    }
068
069    // ---
070
071    @Override
072    public JSONObject toJSON() {
073        try {
074            return new JSONObject()
075                .put("info", topicmapTopic.toJSON())
076                .put("topics", DeepaMehtaUtils.toJSONArray(topics.values()))
077                .put("assocs", DeepaMehtaUtils.toJSONArray(assocs.values()));
078        } catch (Exception e) {
079            throw new RuntimeException("Serialization failed (" + this + ")", e);
080        }
081    }
082
083    @Override
084    public String toString() {
085        return "topicmap " + getId();
086    }
087
088    // -------------------------------------------------------------------------------------------------- Nested Classes
089
090    // Note: there is a client-side equivalent in canvas_view.js (deepamehta-webclient plugin)
091    public class GridPositioning {
092
093        // Settings
094        private final int GRID_DIST_X = 220;    // MAX_TOPIC_LABEL_WIDTH + 20 pixel padding
095        private final int GRID_DIST_Y = 80;
096        private final int START_X;
097        private final int START_Y = 50;
098        private final int MIN_Y = -9999;
099
100        private final int canvasWidth;
101        private final int transX;
102
103        private int gridX;
104        private int gridY;
105
106        // --- Constructors ---
107
108        public GridPositioning(int canvasWidth, int transX) {
109            this.canvasWidth = canvasWidth;
110            this.transX = transX;
111            START_X = 50 - transX;
112            //
113            Point startPos = findStartPostition();
114            gridX = startPos.x;
115            gridY = startPos.y;
116        }
117
118        // --- Public Methods ---
119
120        public Point nextPosition() {
121            Point pos = new Point(gridX, gridY);
122            advancePosition();
123            return pos;
124        }
125
126        // --- Private Methods ---
127
128        private Point findStartPostition() {
129            int maxY = MIN_Y;
130            for (TopicViewModel topic : topics.values()) {
131                if (topic.getY() > maxY) {
132                    maxY = topic.getY();
133                }
134            }
135            int x = START_X;
136            int y = maxY != MIN_Y ? maxY + GRID_DIST_Y : START_Y;
137            return new Point(x, y);
138        }
139
140        private void advancePosition() {
141            if (gridX + GRID_DIST_X + transX > canvasWidth) {
142                gridX = START_X;
143                gridY += GRID_DIST_Y;
144            } else {
145                gridX += GRID_DIST_X;
146            }
147        }
148    }
149}