Mercurial Hosting > nabble
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