Bump copyright years
[cacert-votebot.git] / src / main / java / org / cacert / votebot / shared / CAcertVoteMechanics.java
1 /*
2 * Copyright (c) 2015 Felix Doerre
3 * Copyright (c) 2015 Benny Baumann
4 * Copyright (c) 2016 Jan Dittberner
5 *
6 * This file is part of CAcert votebot.
7 *
8 * CAcert votebot is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation, either version 3 of the License, or (at your option)
11 * any later version.
12 *
13 * CAcert votebot is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * CAcert votebot. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 package org.cacert.votebot.shared;
23
24 import org.springframework.stereotype.Component;
25
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.ResourceBundle;
30
31 /**
32 * Represents the voting-automate for voting in IRC channels.
33 */
34 @Component
35 public final class CAcertVoteMechanics {
36 private static final String PROXY_RE = "^\\s*proxy\\s.*";
37 private static final int VOTE_MESSAGE_PART_COUNT = 3;
38
39 private State state = State.IDLE;
40 private String topic;
41 private final Map<String, VoteType> votes = new HashMap<>();
42 private final ResourceBundle resourceBundle = ResourceBundle.getBundle("messages");
43
44 /**
45 * Voting state indicating whether a vote is currently running or not.
46 */
47 public enum State {
48 /**
49 * A vote is currently running.
50 */
51 RUNNING,
52 /**
53 * No vote is running.
54 */
55 IDLE
56 }
57
58 private String vote(final String voter, final String actor, final VoteType type) {
59 votes.put(voter, type);
60
61 if (voter.equals(actor)) {
62 return String.format(resourceBundle.getString("count_vote"), actor, type);
63 } else {
64 return String.format(resourceBundle.getString("count_proxy_vote"), actor, voter, type);
65 }
66 }
67
68 private String voteError(final String actor) {
69 return String.format(resourceBundle.getString("vote_not_understood"), actor);
70 }
71
72 private String proxyVoteError(final String actor) {
73 return String.format(resourceBundle.getString("invalid_proxy_vote"), actor);
74 }
75
76 /**
77 * Adds a vote to the current topic. This interprets proxies.
78 *
79 * @param actor the person that sent this vote
80 * @param txt the text that the person sent.
81 * @return A message to <code>actor</code> indicating the result of his action.
82 */
83 public synchronized String evaluateVote(final String actor, final String txt) {
84 if (state != State.RUNNING) {
85 return String.format(resourceBundle.getString("no_vote_running"), actor);
86 }
87
88 final String voter;
89 final String value;
90
91 if (txt.toLowerCase().matches(PROXY_RE)) {
92 String[] parts = txt.split("\\s+");
93 if (parts.length == VOTE_MESSAGE_PART_COUNT) {
94 voter = parts[1];
95 value = parts[2];
96 } else {
97 return proxyVoteError(actor);
98 }
99 } else {
100 voter = actor;
101 value = txt.trim();
102 }
103
104 try {
105 return vote(voter, actor, VoteType.evaluate(value));
106 } catch (IllegalArgumentException iae) {
107 return voteError(actor);
108 }
109 }
110
111 /**
112 * A new vote begins.
113 *
114 * @param topic the topic of the vote
115 * @return A response to <code>from</code> indicating success or failure.
116 */
117 public synchronized String callVote(final String topic) {
118 if (state != State.IDLE) {
119 return resourceBundle.getString("vote_running");
120 }
121
122 this.topic = topic;
123 votes.clear();
124
125 state = State.RUNNING;
126
127 return resourceBundle.getString("vote_started");
128 }
129
130 /**
131 * Ends a vote.
132 *
133 * @return An array of Strings containing result status messages.
134 */
135 public synchronized String[] closeVote() {
136 final int[] resultCounts = new int[VoteType.values().length];
137
138 for (final Entry<String, VoteType> voteEntry : votes.entrySet()) {
139 resultCounts[voteEntry.getValue().ordinal()]++;
140 }
141
142 final String[] results = new String[VoteType.values().length];
143
144 for (int i = 0; i < results.length; i++) {
145 results[i] = String.format("%s: %d", VoteType.values()[i], resultCounts[i]);
146 }
147
148 votes.clear();
149 state = State.IDLE;
150 topic = "";
151
152 return results;
153 }
154
155 /**
156 * @return Topic of the current vote.
157 */
158 public String getTopic() {
159 return topic;
160 }
161
162 /**
163 * @return Voting state
164 */
165 public State getState() {
166 return state;
167 }
168
169 /**
170 * @return current vote results as string
171 */
172 public String getCurrentResult() {
173 return votes.toString();
174 }
175
176 }