diff src/nabble/modules/poll/Poll.java @ 0:7ecd1a4ef557

add content
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 21 Mar 2019 19:15:52 -0600
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/nabble/modules/poll/Poll.java	Thu Mar 21 19:15:52 2019 -0600
@@ -0,0 +1,262 @@
+package nabble.modules.poll;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import fschmidt.db.DbNull;
+import fschmidt.db.DbRecord;
+import fschmidt.db.LongKey;
+import nabble.model.ModelException;
+import nabble.model.Node;
+import nabble.model.User;
+import nabble.model.ExtensionFactory;
+import nabble.model.ModelHome;
+
+
+final class Poll {
+
+
+	private static final ExtensionFactory<Node,Poll> FACTORY = new ExtensionFactory<Node,Poll>() {
+
+		public String getName() {
+			return PollModule.INSTANCE.getName();
+		}
+
+		public Class<Poll> extensionClass() {
+			return Poll.class;
+		}
+
+		public Poll construct(Node node) {
+			return null;
+		}
+
+		public Poll construct(Node node,ResultSet rs)
+			throws SQLException
+		{
+			int pollOptionCount = rs.getInt("poll_option_count");
+			if( pollOptionCount==0 )
+				return null;
+			return new Poll(node,pollOptionCount);
+		}
+
+		public Serializable getExportData(Node node) {
+			return null; //throw new UnsupportedOperationException();
+		}
+
+		public void saveExportData(Node node,Serializable s) {
+			//throw new UnsupportedOperationException();
+		}
+	};
+
+	static {
+		ModelHome.addNodeExtensionFactory(FACTORY);
+	}
+
+	static void init() {}
+
+	public static Poll of(Node node) {
+		return node.getExtension(FACTORY);
+	}
+
+
+
+
+
+	private final Node node;
+	private int pollOptionCount;
+	private String question;
+	private String[] options;
+	private int maxChoices = 1;
+	private Date endDate = null;
+	private boolean allowVoteChange = true;
+	private boolean showResultsBeforeVote = true;
+	private boolean showResultsBeforeEnd = true;
+
+	Poll(Node node,int pollOptionCount) {
+		this.node = node;
+		this.pollOptionCount = pollOptionCount;
+		this.question = node.getProperty("poll_question");
+		this.options = new String[pollOptionCount];
+		for (int i=0; i<options.length; i++)
+			this.options[i] = node.getProperty("poll_option_"+i);
+		String maxChoicesS = node.getProperty("poll_max_choices");
+		maxChoices = maxChoicesS!=null ? Integer.parseInt(maxChoicesS) : 1;
+		String endtime = node.getProperty("poll_end_date");
+		endDate = endtime == null ? null : new Date(Long.parseLong(endtime));
+		allowVoteChange = !"false".equals(node.getProperty("poll_allow_vote_change"));
+		showResultsBeforeVote = !"false".equals(node.getProperty("poll_show_results_before_vote"));
+		showResultsBeforeEnd = !"false".equals(node.getProperty("poll_show_results_before_end"));
+	}
+
+	void set(String question, String[] options) throws PollFormatException {
+		if( !node.getSite().getDb().isInTransaction() )
+			throw new RuntimeException("not in transaction");
+		if (question.trim().length()==0 || options.length < 2)
+			throw new PollFormatException();
+		this.pollOptionCount = options.length;
+		DbRecord<LongKey,?> record = node.getDbRecord();
+		record.fields().put("poll_option_count", pollOptionCount);
+		record.update();
+		this.question = question.trim();
+		node.setProperty("poll_question", question.trim());
+		this.options = new String[options.length];
+		for (int i=0; i<options.length; i++) {
+			String option = options[i].trim();
+			if (option.length()==0)
+				throw new PollFormatException();
+			this.options[i] = option;
+			node.setProperty("poll_option_"+i, option);
+		}
+		clearVotes();
+	}
+
+	Node getNode() {
+		return node;
+	}
+
+	String getQuestion() {
+		return question;
+	}
+
+	String[] getOptions() {
+		return options;
+	}
+
+	int[] getVoteCounts() {
+		int[] votes = new int[options.length];
+		for (int i=0; i<options.length; i++) {
+			votes[i] = node.getSite().countTags("node_id="+node.getId()+"and label='"+VOTE_LABEL_PREFIX+i+"'");
+		}
+		return votes;
+	}
+
+	private static final String VOTE_LABEL_PREFIX="poll_vote:";
+
+	int[] getVotes(User user) {
+		List<Integer> v = new ArrayList<Integer>();
+		for (int i=0; i<options.length; i++) {
+			boolean vote = node.getSite().hasTags(node, user, "label='"+VOTE_LABEL_PREFIX+i+"'");
+			if (vote) v.add(i);  // duplicates aren't possible because the index is unique
+		}
+		int[] votes = new int[v.size()];
+		for (int i=0; i<votes.length; i++)
+			votes[i] = v.get(i);
+		return votes;
+		/*
+		List<String> labels = node.getSite().findTagLabels(
+				"node_id="+node.getId()+" and user_id="+user.getId()+" and label like '"+VOTE_LABEL_PREFIX+"%'"
+				);
+		int[] votes = new int[labels.size()];
+		for (int i=0; i<votes.length; i++) {
+			votes[i] = Integer.parseInt(labels.get(i).substring(VOTE_LABEL_PREFIX.length()));
+		}
+		return votes;
+		*/
+	}
+
+	void vote(User user, int[] votes) throws PollVoteException {
+		if (votes.length > maxChoices)
+			throw new PollVoteException();
+		if (!allowVoteChange && getVotes(user).length>0)
+			throw new PollVoteException();
+		if (endDate!=null && endDate.before(new Date()))
+			throw new PollVoteException();
+		node.getSite().deleteTags(node, user, "label like '"+VOTE_LABEL_PREFIX+"%'");
+		for (int vote : votes) {
+			node.getSite().addTag(node, user, VOTE_LABEL_PREFIX+vote);
+		}
+	}
+
+	private void clearVotes() {
+		node.getSite().deleteTags(
+				"node_id="+node.getId()+" and label like '"+VOTE_LABEL_PREFIX+"%'"
+				);
+	}
+
+	void delete() {
+		if( !node.getSite().getDb().isInTransaction() )
+			throw new RuntimeException("not in transaction");
+		node.setProperty("poll_question", null);
+		for (int i=0; i<pollOptionCount; i++) {
+			node.setProperty("poll_option_"+i, null);
+		}
+		node.setProperty("poll_max_choices", null);
+		node.setProperty("poll_end_date", null);
+		node.setProperty("poll_allow_vote_change", null);
+		node.setProperty("poll_show_results_before_vote", null);
+		node.setProperty("poll_show_results_before_end", null);
+		clearVotes();
+		DbRecord<LongKey,?> record = node.getDbRecord();
+		record.fields().put("poll_option_count", DbNull.INTEGER);
+		record.update();
+	}
+
+
+	int maxChoices() {
+		return maxChoices;
+	}
+
+	Date endDate() {
+		return endDate;
+	}
+
+	boolean allowVoteChange() {
+		return allowVoteChange;
+	}
+
+	boolean showResultsBeforeVote() {
+		return showResultsBeforeVote;
+	}
+
+	boolean showResultsBeforeEnd() {
+		return showResultsBeforeEnd;
+	}
+
+	void setMaxChoices(int maxChoices) throws PollFormatException {
+		if (maxChoices < 1 || maxChoices > options.length) throw new PollFormatException();
+		this.maxChoices = maxChoices;
+		node.setProperty("poll_max_choices", maxChoices > 1 ? String.valueOf(maxChoices) : null);
+	}
+
+	void setEndDate(Date endDate) {
+		this.endDate = endDate;
+		node.setProperty("poll_end_date", endDate!=null ? String.valueOf(endDate.getTime()) : null);
+	}
+
+	void setAllowVoteChange(boolean allowVoteChange) {
+		node.setProperty("poll_allow_vote_change", allowVoteChange ? null : "false");
+		this.allowVoteChange = allowVoteChange;
+	}
+
+	void setShowResultsBeforeVote(boolean showResultsBeforeVote) {
+		node.setProperty("poll_show_results_before_vote", showResultsBeforeVote ? null : "false");
+		this.showResultsBeforeVote = showResultsBeforeVote;
+	}
+
+	void setShowResultsBeforeEnd(boolean showResultsBeforeEnd) {
+		node.setProperty("poll_show_results_before_end", showResultsBeforeEnd ? null : "false");
+		this.showResultsBeforeEnd = showResultsBeforeEnd;
+	}
+
+	public static class PollFormatException extends ModelException {
+		public PollFormatException() {
+			super(name("invalid_poll_format"), "Invalid poll parameters.");
+		}
+	}
+	
+	public static class PollEditException extends ModelException {
+		public PollEditException() {
+			super(name("poll_edit_disallowed"), "You cannot modify this poll.");
+		}
+	}
+
+	public static class PollVoteException extends ModelException {
+		public PollVoteException() {
+			super(name("invalid_vote_attempt"), "Invalid voting attempt.");
+		}
+	}
+
+}
\ No newline at end of file