View Javadoc
1   /*
2    * Copyright (C) 2003-2007 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.services.cms.voting.impl;
18  
19  import java.text.DecimalFormat;
20  
21  import javax.jcr.Node;
22  import javax.jcr.NodeIterator;
23  import javax.jcr.Session;
24  import javax.jcr.Value;
25  import javax.jcr.nodetype.NoSuchNodeTypeException;
26  
27  import org.exoplatform.services.cms.i18n.MultiLanguageService;
28  import org.exoplatform.services.cms.voting.VotingService;
29  import org.exoplatform.services.jcr.core.ManageableRepository;
30  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
31  
32  /**
33   * Created by The eXo Platform SAS
34   * Author : Pham Xuan Hoa
35   *          hoa.pham@exoplatform.com
36   * Jan 17, 2007
37   */
38  public class VotingServiceImpl implements VotingService {
39  
40    final static String VOTABLE = "mix:votable";
41    final static String VOTER_PROP = "exo:voter";
42    final static String VOTER_VOTEVALUE_PROP = "exo:voterVoteValues";
43    final static String VOTING_RATE_PROP = "exo:votingRate";
44    final static String VOTE_TOTAL_PROP = "exo:voteTotal";
45    final static String VOTE_TOTAL_LANG_PROP = "exo:voteTotalOfLang";
46    private static final String LANGUAGES = "languages";
47    private static final String SPACE = " ";
48  
49    private MultiLanguageService multiLangService_ ;
50  
51    public VotingServiceImpl(MultiLanguageService multiLangService) {
52      multiLangService_ = multiLangService ;
53    }
54  
55    /**
56     * {@inheritDoc}
57     */
58    public long getVoteTotal(Node node) throws Exception {
59      long voteTotal = 0;
60      if(!node.hasNode(LANGUAGES) && node.hasProperty(VOTE_TOTAL_PROP)) {
61        return node.getProperty(VOTE_TOTAL_LANG_PROP).getLong() ;
62      }
63      Node multiLanguages = node.getNode(LANGUAGES) ;
64      voteTotal = node.getProperty(VOTE_TOTAL_LANG_PROP).getLong() ;
65      NodeIterator nodeIter = multiLanguages.getNodes() ;
66      while(nodeIter.hasNext()) {
67        Node languageNode = nodeIter.nextNode() ;
68        if(node.isNodeType("nt:file")) {
69          languageNode = getFileLangNode(languageNode) ;
70        }
71        if(languageNode.hasProperty(VOTE_TOTAL_LANG_PROP)) {
72          voteTotal = voteTotal + languageNode.getProperty(VOTE_TOTAL_LANG_PROP).getLong() ;
73        }
74      }
75      return voteTotal ;
76    }
77  
78    /**
79     * Getting node is "nt:file" node type.
80     * @param currentNode
81     * @return
82     */
83    public Node getFileLangNode(Node currentNode) throws Exception {
84      if(currentNode.getNodes().getSize() > 0) {
85        NodeIterator nodeIter = currentNode.getNodes() ;
86        while(nodeIter.hasNext()) {
87          Node ntFile = nodeIter.nextNode() ;
88          if(ntFile.isNodeType("nt:file")) {
89            return ntFile ;
90          }
91        }
92        return currentNode ;
93      }
94      return currentNode ;
95    }
96  
97    /**
98     * {@inheritDoc}
99     */
100   public void vote(Node node, double rate, String userName, String language) throws Exception {
101     Session session = node.getSession();
102     node = handleUser(session, node, userName);
103     //add mixin exo:votable
104     if(!node.isNodeType(VOTABLE)) {
105       if(node.canAddMixin(VOTABLE)) node.addMixin(VOTABLE) ;
106       else throw new NoSuchNodeTypeException() ;
107     }
108 
109     Node languageNode = handleLanguage(node, language);
110     if(!languageNode.isNodeType(VOTABLE)) {
111       if(languageNode.canAddMixin(VOTABLE)) languageNode.addMixin(VOTABLE) ;
112       else throw new NoSuchNodeTypeException() ;
113     }
114     // get data to calculate
115     long voteTotalOfLang = languageNode.getProperty(VOTE_TOTAL_LANG_PROP).getLong() ;
116     double votingRate = languageNode.getProperty(VOTING_RATE_PROP).getDouble() ;
117     Value voterVoteValue = getVoterVoteValue(languageNode, userName);
118 
119     //set new vote rating
120     double newRating = 0;
121     if (voterVoteValue != null) {
122       String oldUserRateValueStr = voterVoteValue.getString().substring(voterVoteValue.getString().lastIndexOf(SPACE) + 1);
123       double oldUserRate = Double.parseDouble(oldUserRateValueStr);
124       newRating = ((voteTotalOfLang*votingRate)+rate - oldUserRate)/(voteTotalOfLang) ;
125     } else {
126       newRating = ((voteTotalOfLang*votingRate)+rate)/(voteTotalOfLang+1) ;
127     }
128     DecimalFormat format = new DecimalFormat("###.##") ;
129     double formatedRating= format.parse(format.format(newRating)).doubleValue() ;
130 
131     //set new voters
132     Value[] voterVoteValues = {} ;
133     Value[] newVoterVoteValues = null;
134     if(languageNode.hasProperty(VOTER_VOTEVALUE_PROP)) {
135       voterVoteValues = languageNode.getProperty(VOTER_VOTEVALUE_PROP).getValues() ;
136     }
137     if (voterVoteValue != null) {
138       newVoterVoteValues = replaceOldValue(voterVoteValues, userName, rate, languageNode.getSession());
139     } else {
140       Value newVoterVoteValue = languageNode.getSession().getValueFactory().createValue(userName + SPACE + rate);
141       newVoterVoteValues = new Value[voterVoteValues.length + (userName == null ? 0 : 1)];
142       System.arraycopy(voterVoteValues, 0, newVoterVoteValues, 0, voterVoteValues.length);
143       if (userName != null)
144         newVoterVoteValues[voterVoteValues.length] = newVoterVoteValue;
145     }
146     languageNode.setProperty(VOTER_VOTEVALUE_PROP, newVoterVoteValues);
147 
148     //set total vote size and vote rate.
149     node.setProperty(VOTE_TOTAL_PROP,getVoteTotal(node)+(voterVoteValue == null ? 1 : 0)) ;
150     languageNode.setProperty(VOTE_TOTAL_LANG_PROP,voteTotalOfLang+(voterVoteValue == null ? 1 : 0)) ;
151     languageNode.setProperty(VOTING_RATE_PROP,formatedRating) ;
152 
153     node.getSession().save() ;
154     languageNode.getSession().save();
155   }
156 
157   //get the VoterVoteValue object from node
158   private Value getVoterVoteValue(Node languageNode, String userName) throws Exception {
159     if (!languageNode.hasProperty(VOTER_VOTEVALUE_PROP)) {
160       return null;
161     }
162     Value ret = null;
163     for (Value voterVoteValue : languageNode.getProperty(VOTER_VOTEVALUE_PROP).getValues()) {
164       if (voterVoteValue.getString().startsWith(userName + SPACE)) {
165         return voterVoteValue;
166       }
167     }
168     return ret;
169   }
170 
171   //replace old VoterVoteValue by the new one
172   private Value[] replaceOldValue(Value[] voterVoteValues, String userName, double rate, Session session)
173     throws Exception {
174     for (int i = 0; i < voterVoteValues.length; i++) {
175       if (voterVoteValues[i].getString().startsWith(userName + SPACE)) {
176         voterVoteValues[i] = session.getValueFactory().createValue(userName + SPACE + rate);
177         break;
178       }
179     }
180     return voterVoteValues;
181   }
182   public boolean isVoted(Node node, String userName, String language) throws Exception {
183     return getVoteValueOfUser(node, userName, language) > 0;
184   }
185 
186   private Node handleLanguage(Node node, String language) throws Exception {
187     String defaultLang = multiLangService_.getDefault(node) ;
188     Node multiLanguages =null, languageNode= null ;
189     if((defaultLang == null && language == null) || language.equals(defaultLang)) {
190       languageNode = node ;
191     } else {
192       if(node.hasNode(LANGUAGES)) {
193         multiLanguages = node.getNode(LANGUAGES) ;
194         if(multiLanguages.hasNode(language)) {
195           languageNode = multiLanguages.getNode(language) ;
196           if(node.isNodeType("nt:file")) {
197             languageNode = getFileLangNode(languageNode) ;
198           }
199         }
200       } else {
201       languageNode = node;
202     }
203     }
204     return languageNode;
205   }
206 
207   private Node handleUser(Session session, Node node, String userName) throws Exception {
208     if (userName == null || "__anonim".equals(userName)) {
209       String strWorkspaceName = node.getSession().getWorkspace().getName();
210       ManageableRepository manageRepository = WCMCoreUtils.getRepository();
211       session = WCMCoreUtils.getSystemSessionProvider().getSession(strWorkspaceName,
212           manageRepository);
213       String uid = node.getUUID();
214       node = session.getNodeByUUID(uid);
215     }
216     return node;
217   }
218 
219   @Override
220   public double getVoteValueOfUser(Node node, String userName, String language) throws Exception {
221     Session session = node.getSession();
222     node = handleUser(session, node, userName);
223 
224     if(!node.isNodeType(VOTABLE)) {
225       if(node.canAddMixin(VOTABLE)) node.addMixin(VOTABLE) ;
226       else throw new NoSuchNodeTypeException() ;
227     }
228 
229     Node languageNode = handleLanguage(node, language);
230     Value voterVoteValue = getVoterVoteValue(languageNode, userName);
231     if (voterVoteValue != null) {
232       String stValue = voterVoteValue.getString();
233       return Double.parseDouble(stValue.substring(stValue.indexOf(SPACE) + 1));
234     } 
235     return 0;
236   }
237 }