view 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 source

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.");
		}
	}

}