Index: lams_common/src/java/org/lamsfoundation/lams/tool/OutputDefinitionFactory.java
===================================================================
diff -u
--- lams_common/src/java/org/lamsfoundation/lams/tool/OutputDefinitionFactory.java (revision 0)
+++ lams_common/src/java/org/lamsfoundation/lams/tool/OutputDefinitionFactory.java (revision 530c208895bfea0ba15e8bd8f1c799b163c059c9)
@@ -0,0 +1,223 @@
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+/* $Id$ */
+package org.lamsfoundation.lams.tool;
+
+import java.util.Locale;
+import java.util.SortedMap;
+
+import org.apache.log4j.Logger;
+import org.lamsfoundation.lams.tool.exception.ToolException;
+import org.lamsfoundation.lams.util.ILoadedMessageSourceService;
+import org.lamsfoundation.lams.util.MessageService;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.NoSuchMessageException;
+import org.springframework.context.i18n.LocaleContextHolder;
+
+/**
+ * This class forms the basic implementation of an output definition factory, which is the class in a tool that
+ * creates the output definitions for a tool. Each tool that has outputs should create its own factory class that
+ * extends this class and uses the methods implemented in this class to create the actual ToolOutputDefinition objects.
+ *
+ * The implemented factory (in the tool) needs to supply either (a) its own messageService bean (a Spring bean normally
+ * defined in the tool's applicationContext file in the toolMessageService property or (b) the name of its I18N language package/filename AND
+ * the the loadedMessageSourceService (which is defined as a Spring bean in the core context). One of the two options (but not
+ * both) is required so that the getDescription() method can get the internationalised description of the output definition
+ * from the tool's internationalisation files. If neither the messageService or the I18N name is not supplied then the name
+ * if the output definition will appear in the description field.
+ *
+ * Example definitions for tool factories:
+ *
+ *
+ * org.lamsfoundation.lams.tool.mc.ApplicationResources
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+public abstract class OutputDefinitionFactory {
+
+ protected Logger log = Logger.getLogger(OutputDefinitionFactory.class);
+
+ private MessageService toolMessageService;
+
+ private ILoadedMessageSourceService loadedMessageSourceService;
+ private String languageFilename;
+ protected final String KEY_PREFIX = "output.desc.";
+
+ /** Create a map of the tool output definitions, suitable for returning from the method
+ * getToolOutputDefinitions() in the ToolContentManager interface. The class for the toolContentObject
+ * will be unique to each tool e.g. for Multiple Choice tool it will be the McContent object.
+ *
+ * If the toolContentObject should not be null - if the toolContentId supplied in the call to
+ * getToolOutputDefinitions(Long toolContentId) does not match to any existing content, the content
+ * should be the tool's default tool content.
+ *
+ * @param toolContentObject
+ * @return SortedMap of ToolOutputDefinitions where the key is the name from each definition
+ * @throws ToolException
+ */
+ public abstract SortedMap getToolOutputDefinitions(Object toolContentObject) throws ToolException;
+
+ /** Tool specific toolMessageService, such as the forumMessageService in the Forum tool */
+ public MessageService getToolMessageService() {
+ return toolMessageService;
+ }
+
+ public void setToolMessageService(MessageService toolMessageService) {
+ this.toolMessageService = toolMessageService;
+ }
+
+
+ /** Set the common loadedMessageSourceService, based on the bean defined in the common Spring context.
+ * If toolMessageService is not set, then the languageFilename
+ * and loadedMessageSourceService should be set. */
+ public ILoadedMessageSourceService getLoadedMessageSourceService() {
+ return loadedMessageSourceService;
+ }
+
+ public void setLoadedMessageSourceService(
+ ILoadedMessageSourceService loadedMessageSourceService) {
+ this.loadedMessageSourceService = loadedMessageSourceService;
+ }
+
+ /** Set the filename/path for the tool's I18N files. If toolMessageService is not set, then the languageFilename
+ * and loadedMessageSourceService should be set. */
+ public String getLanguageFilename() {
+ return languageFilename;
+ }
+
+ /** Get the filename and path for the tool's I18N files. */
+ public void setLanguageFilename(String languageFilename) {
+ this.languageFilename = languageFilename;
+ }
+
+ /**
+ * Get the I18N description for this definitionName. If the tool has supplied a messageService, then this
+ * is used to look up the key and hence get the description. Otherwise if the tool has supplied a I18N
+ * languageFilename then it is accessed via the shared toolActMessageService. If neither are supplied or
+ * the key is not found, then any "." in the name are converted to space and this is used as the description.
+ *
+ * The key must be in the format output.desc.[definition name]. For example a
+ * definition name of "learner.mark" becomes output.desc.learner.mark.
+ */
+ protected String getDescription(String definitionName) {
+ MessageSource msgSource = null;
+ if ( getToolMessageService() != null ) {
+ msgSource = getToolMessageService().getMessageSource();
+ }
+ if ( msgSource == null && getLoadedMessageSourceService() != null && getLanguageFilename() != null) {
+ msgSource = getLoadedMessageSourceService().getMessageService(getLanguageFilename());
+ }
+ if ( msgSource == null ) {
+ log.warn("Unable to internationalise the description for the output definition "+definitionName+" as no MessageSource is available. "+
+ "The tool's OutputDefinition factory needs to set either (a) messageSource or (b) loadedMessageSourceService and languageFilename.");
+ }
+
+ String description = null;
+ if ( msgSource != null ) {
+ String key = KEY_PREFIX + definitionName;
+ Locale locale = LocaleContextHolder.getLocale();
+ try {
+ description = msgSource.getMessage(key,null,locale);
+ } catch ( NoSuchMessageException e ) {
+ }
+ }
+ if ( description == null || description.length() == 0 ) {
+ description = definitionName.replace('.', ' ');
+ }
+
+ return description;
+ }
+
+ /** Generic method for building a tool output definition. It will get the definition's description
+ * from the I18N file using the getDescription() method.
+ * Only use if the other buildBlahDefinitions do not suit your needs. */
+ protected ToolOutputDefinition buildDefinition(String definitionName, OutputType type, Object startValue, Object endValue, Object complexValue) {
+ ToolOutputDefinition definition = new ToolOutputDefinition();
+ definition.setName(definitionName);
+ definition.setDescription(getDescription(definitionName));
+ definition.setType(type);
+ definition.setStartValue( startValue );
+ definition.setEndValue( endValue );
+ definition.setComplexDefinition( complexValue );
+ return definition;
+ }
+
+ //The mark for a user's last attempt at answering the question(s).
+ /** Build a tool definition designed for a range of integer values.
+ * It will get the definition's description from the I18N file using the getDescription() method and
+ * set the type to OUTPUT_LONG. */
+ protected ToolOutputDefinition buildRangeDefinition(String definitionName, Long startValue, Long endValue) {
+ return buildDefinition(definitionName, OutputType.OUTPUT_LONG, startValue, endValue, null);
+ }
+
+ /** Build a tool definition designed for a range of string values.
+ * It will get the definition's description from the I18N file using the getDescription() method and
+ * set the type to OUTPUT_LONG. */
+ protected ToolOutputDefinition buildRangeDefinition(String definitionName, String startValue, String endValue) {
+ return buildDefinition(definitionName, OutputType.OUTPUT_STRING, startValue, endValue, null);
+ }
+
+ /** Build a tool definition designed for a single double value, which is likely to be a statistic number of
+ * questions answered
+ * It will get the definition's description from the I18N file using the getDescription() method and
+ * set the type to OUTPUT_LONG. */
+ protected ToolOutputDefinition buildLongOutputDefinition(String definitionName) {
+ return buildDefinition(definitionName, OutputType.OUTPUT_LONG, null, null, null);
+ }
+
+ /** Build a tool definition designed for a single double value, which is likely to be a statistic such as average
+ * number of posts.
+ * It will get the definition's description from the I18N file using the getDescription() method and
+ * set the type to OUTPUT_DOUBLE. */
+ protected ToolOutputDefinition buildDoubleOutputDefinition(String definitionName) {
+ return buildDefinition(definitionName, OutputType.OUTPUT_DOUBLE, null, null, null);
+ }
+
+ /** Build a tool definition designed for a single boolean value, which is likely to be a test such as
+ * user has answered all questions correctly.
+ * It will get the definition's description from the I18N file using the getDescription() method and
+ * set the type to OUTPUT_BOOLEAN. */
+ protected ToolOutputDefinition buildBooleanOutputDefinition(String definitionName) {
+ return buildDefinition(definitionName, OutputType.OUTPUT_BOOLEAN, null, null, null);
+ }
+
+ /** Build a tool definition designed for a single String value.
+ * It will get the definition's description from the I18N file using the getDescription() method and
+ * set the type to OUTPUT_STRING. */
+ protected ToolOutputDefinition buildStringOutputDefinition(String definitionName) {
+ return buildDefinition(definitionName, OutputType.OUTPUT_STRING, null, null, null);
+ }
+
+ /** Build a tool definition for a complex value output.
+ * It will get the definition's description from the I18N file using the getDescription() method and
+ * set the type to OUTPUT_COMPLEX. */
+ protected ToolOutputDefinition buildComplexOutputDefinition(String definitionName) {
+ return buildDefinition(definitionName, OutputType.OUTPUT_COMPLEX, null, null, null);
+ }
+
+}
Index: lams_common/src/java/org/lamsfoundation/lams/tool/ToolContentManager.java
===================================================================
diff -u -r9481bb9c6f8c0e4d6fbed6b230a41c77feda64c6 -r530c208895bfea0ba15e8bd8f1c799b163c059c9
--- lams_common/src/java/org/lamsfoundation/lams/tool/ToolContentManager.java (.../ToolContentManager.java) (revision 9481bb9c6f8c0e4d6fbed6b230a41c77feda64c6)
+++ lams_common/src/java/org/lamsfoundation/lams/tool/ToolContentManager.java (.../ToolContentManager.java) (revision 530c208895bfea0ba15e8bd8f1c799b163c059c9)
@@ -23,6 +23,9 @@
/* $$Id$$ */
package org.lamsfoundation.lams.tool;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
import org.lamsfoundation.lams.tool.exception.DataMissingException;
import org.lamsfoundation.lams.tool.exception.SessionDataExistsException;
import org.lamsfoundation.lams.tool.exception.ToolException;
@@ -116,5 +119,13 @@
public void importToolContent(Long toolContentId, Integer newUserUid, String toolContentPath, String fromVersion, String toVersion)
throws ToolException;
-
+ /** Get the definitions for possible output for an activity, based on the toolContentId. These may be definitions that are always
+ * available for the tool (e.g. number of marks for Multiple Choice) or a custom definition created for a particular activity
+ * such as the answer to the third question contains the word Koala and hence the need for the toolContentId
+ * @return SortedMap of ToolOutputDefinitions with the key being the name of each definition.
+ *
+ * Added in LAMS 2.1
+ */
+ public SortedMap getToolOutputDefinitions(Long toolContentId) throws ToolException;
+
}
Index: lams_common/src/java/org/lamsfoundation/lams/tool/ToolOutputDefinition.java
===================================================================
diff -u -r09048f91f2dcbb6b63449f3c1fb9e1a09221a35e -r530c208895bfea0ba15e8bd8f1c799b163c059c9
--- lams_common/src/java/org/lamsfoundation/lams/tool/ToolOutputDefinition.java (.../ToolOutputDefinition.java) (revision 09048f91f2dcbb6b63449f3c1fb9e1a09221a35e)
+++ lams_common/src/java/org/lamsfoundation/lams/tool/ToolOutputDefinition.java (.../ToolOutputDefinition.java) (revision 530c208895bfea0ba15e8bd8f1c799b163c059c9)
@@ -23,6 +23,7 @@
/* $Id$ */
package org.lamsfoundation.lams.tool;
+import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
@@ -49,7 +50,7 @@
* complexDefinition = null;
* }
*/
-public class ToolOutputDefinition {
+public class ToolOutputDefinition implements Comparable {
private String name;
private String description;
@@ -129,22 +130,25 @@
ToolOutputDefinition castOther = (ToolOutputDefinition) other;
return new EqualsBuilder()
.append(this.name, castOther.name)
- .append(this.description, castOther.description)
.append(this.type, castOther.type)
- .append(this.startValue, castOther.startValue)
- .append(this.endValue, castOther.endValue)
.isEquals();
}
public int hashCode() {
return new HashCodeBuilder()
.append(name)
- .append(description)
.append(type)
- .append(startValue)
- .append(endValue)
.toHashCode();
}
+ public int compareTo(Object o) {
+
+ ToolOutputDefinition myClass = (ToolOutputDefinition) o;
+ return new CompareToBuilder()
+ .append(this.name, myClass.name)
+ .append(this.type, myClass.type)
+ .toComparison();
+ }
+
}
Index: lams_common/src/java/org/lamsfoundation/lams/tool/dto/ToolOutputDefinitionDTO.java
===================================================================
diff -u -rfb25ea600503d3eea142b305696081f882d6552a -r530c208895bfea0ba15e8bd8f1c799b163c059c9
--- lams_common/src/java/org/lamsfoundation/lams/tool/dto/ToolOutputDefinitionDTO.java (.../ToolOutputDefinitionDTO.java) (revision fb25ea600503d3eea142b305696081f882d6552a)
+++ lams_common/src/java/org/lamsfoundation/lams/tool/dto/ToolOutputDefinitionDTO.java (.../ToolOutputDefinitionDTO.java) (revision 530c208895bfea0ba15e8bd8f1c799b163c059c9)
@@ -23,6 +23,7 @@
/* $$Id$$ */
package org.lamsfoundation.lams.tool.dto;
+import org.apache.commons.lang.builder.ToStringBuilder;
import org.lamsfoundation.lams.tool.ToolOutputDefinition;
/**
@@ -31,7 +32,7 @@
* This class acts as a data transfer object for
* transferring information between FLASH and the core module.
*
- * This class is required in the authoring enviornment to pass
+ * This class is required in the authoring environment to pass
* information about the ToolOutput for a ToolActivity
*/
public class ToolOutputDefinitionDTO {
@@ -110,4 +111,14 @@
public String getComplexDefinition() {
return complexDefinition;
}
+
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("name", name)
+ .append("description", description)
+ .append("type", type)
+ .append("startValue", startValue)
+ .append("endValue", endValue)
+ .toString();
+ }
}
Index: lams_common/src/java/org/lamsfoundation/lams/tool/service/ILamsCoreToolService.java
===================================================================
diff -u -r9481bb9c6f8c0e4d6fbed6b230a41c77feda64c6 -r530c208895bfea0ba15e8bd8f1c799b163c059c9
--- lams_common/src/java/org/lamsfoundation/lams/tool/service/ILamsCoreToolService.java (.../ILamsCoreToolService.java) (revision 9481bb9c6f8c0e4d6fbed6b230a41c77feda64c6)
+++ lams_common/src/java/org/lamsfoundation/lams/tool/service/ILamsCoreToolService.java (.../ILamsCoreToolService.java) (revision 530c208895bfea0ba15e8bd8f1c799b163c059c9)
@@ -26,10 +26,12 @@
import java.util.List;
import java.util.Set;
+import java.util.SortedMap;
import org.lamsfoundation.lams.learningdesign.Activity;
import org.lamsfoundation.lams.learningdesign.ToolActivity;
import org.lamsfoundation.lams.lesson.Lesson;
+import org.lamsfoundation.lams.tool.ToolOutputDefinition;
import org.lamsfoundation.lams.tool.ToolSession;
import org.lamsfoundation.lams.tool.exception.DataMissingException;
import org.lamsfoundation.lams.tool.exception.LamsToolServiceException;
@@ -167,8 +169,23 @@
* @throws ToolException
*/
public void notifyToolToDeleteContent(ToolActivity toolActivity) throws ToolException;
-
+
/**
+ * Ask a tool for its OutputDefinitions, based on the given toolContentId. If the tool doesn't
+ * have any content matching the toolContentId then it should create the OutputDefinitions based
+ * on the tool's default content.
+ *
+ * This functionality relies on a method added to the Tool Contract in LAMS 2.1, so if the tool
+ * doesn't support the required method, it writes out an error to the log but doesn't throw
+ * an exception - just returns an empty map.
+ *
+ * @param toolContentId
+ * @return SortedMap of ToolOutputDefinitions with the key being the name of each definition
+ * @throws ToolException
+ */
+ public SortedMap getOutputDefinitionsFromTool(Long toolContentId) throws ToolException;
+
+ /**
* Update the tool session data.
* @param toolSession the new tool session object.
*/
Index: lams_common/src/java/org/lamsfoundation/lams/tool/service/LamsCoreToolService.java
===================================================================
diff -u -r9481bb9c6f8c0e4d6fbed6b230a41c77feda64c6 -r530c208895bfea0ba15e8bd8f1c799b163c059c9
--- lams_common/src/java/org/lamsfoundation/lams/tool/service/LamsCoreToolService.java (.../LamsCoreToolService.java) (revision 9481bb9c6f8c0e4d6fbed6b230a41c77feda64c6)
+++ lams_common/src/java/org/lamsfoundation/lams/tool/service/LamsCoreToolService.java (.../LamsCoreToolService.java) (revision 530c208895bfea0ba15e8bd8f1c799b163c059c9)
@@ -27,6 +27,8 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
import org.apache.log4j.Logger;
import org.lamsfoundation.lams.learningdesign.Activity;
@@ -37,6 +39,7 @@
import org.lamsfoundation.lams.tool.ToolContent;
import org.lamsfoundation.lams.tool.ToolContentIDGenerator;
import org.lamsfoundation.lams.tool.ToolContentManager;
+import org.lamsfoundation.lams.tool.ToolOutputDefinition;
import org.lamsfoundation.lams.tool.ToolSession;
import org.lamsfoundation.lams.tool.ToolSessionManager;
import org.lamsfoundation.lams.tool.dao.ISystemToolDAO;
@@ -318,7 +321,53 @@
throw new ToolException(message,e);
}
}
+
/**
+ * Ask a tool for its OutputDefinitions, based on the given toolContentId. If the tool doesn't
+ * have any content matching the toolContentId then it should create the OutputDefinitions based
+ * on the tool's default content.
+ *
+ * This functionality relies on a method added to the Tool Contract in LAMS 2.1, so if the tool
+ * doesn't support the required method, it writes out an error to the log but doesn't throw
+ * an exception - just returns an empty map.
+ *
+ * @param toolContentId
+ * @return SortedMap of ToolOutputDefinitions with the key being the name of each definition
+ * @throws ToolException
+ */
+ public SortedMap getOutputDefinitionsFromTool(Long toolContentId) throws ToolException {
+
+ ToolContent toolContent = (ToolContent) toolContentDAO.find(ToolContent.class, toolContentId);
+ if ( toolContent == null ) {
+ String error = "The toolContentID "+ toolContentId + " is not valid. No such record exists on the database.";
+ log.error(error);
+ throw new DataMissingException(error);
+ }
+
+ Tool tool = toolContent.getTool();
+ if ( tool == null ) {
+ String error = "The tool for toolContentId "+ toolContentId + " is missing.";
+ log.error(error);
+ throw new DataMissingException(error);
+ }
+
+ try {
+ ToolContentManager contentManager = (ToolContentManager) findToolService(tool);
+ return contentManager.getToolOutputDefinitions(toolContentId);
+ } catch ( NoSuchBeanDefinitionException e ) {
+ String message = "A tool which is defined in the database appears to missing from the classpath. Unable to copy the tool content. ToolContentId "+toolContentId;
+ log.error(message,e);
+ throw new ToolException(message,e);
+ } catch ( java.lang.AbstractMethodError e ) {
+ String message = "Tool "+tool.getToolDisplayName()+" doesn't support the getToolOutputDefinitions(toolContentId) method so no output definitions can be accessed.";
+ log.error(message,e);
+ throw new ToolException(message,e);
+ }
+
+ }
+
+
+ /**
* @see org.lamsfoundation.lams.tool.service.ILamsCoreToolService#updateToolSession(org.lamsfoundation.lams.tool.ToolSession)
*/
public void updateToolSession(ToolSession toolSession)
Index: lams_common/src/java/org/lamsfoundation/lams/util/MessageService.java
===================================================================
diff -u -r08950e1090443c3423a3d1c587416a2fccd8bbdf -r530c208895bfea0ba15e8bd8f1c799b163c059c9
--- lams_common/src/java/org/lamsfoundation/lams/util/MessageService.java (.../MessageService.java) (revision 08950e1090443c3423a3d1c587416a2fccd8bbdf)
+++ lams_common/src/java/org/lamsfoundation/lams/util/MessageService.java (.../MessageService.java) (revision 530c208895bfea0ba15e8bd8f1c799b163c059c9)
@@ -26,35 +26,42 @@
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;
-import org.springframework.context.support.MessageSourceAccessor;
/**
* Service class to help Service bean get i18n value quickly. The locale information will get from LAMS_USER
* table. For more detail see org.lamsfoundation.lams.web.filter.LocaleFilter
.
*
* @author Steve.Ni
* @version $Revision$
- * @see org.springframework.context.support.MessageSourceAccessor
+ * @see org.springframework.context.support.MessageSource
*/
public class MessageService {
- private MessageSourceAccessor messageAccessor;
+ private MessageSource messageSource;
/**
* Set MessageSource
from spring IoC.
* @param messageSource
*/
public void setMessageSource(MessageSource messageSource){
- messageAccessor = new MessageSourceAccessor(messageSource);
+ this.messageSource = messageSource;
}
/**
+ * Set MessageSource
from spring IoC.
+ * @param messageSource
+ */
+ public MessageSource getMessageSource(){
+ return this.messageSource;
+ }
+
+ /**
* @see org.springframework.context.support.MessageSourceAccessor#getMessage(java.lang.String)
* @param key
* @return
*/
public String getMessage(String key){
String message;
try {
- message = messageAccessor.getMessage(key,LocaleContextHolder.getLocale());
+ message = messageSource.getMessage(key,null,LocaleContextHolder.getLocale());
} catch (NoSuchMessageException e) {
message = "??" + key + "??";
}
@@ -69,7 +76,7 @@
public String getMessage(String key, String defaultMessage){
String message = defaultMessage;
try {
- message = messageAccessor.getMessage(key,defaultMessage,LocaleContextHolder.getLocale());
+ message = messageSource.getMessage(key,null,defaultMessage,LocaleContextHolder.getLocale());
} catch (NoSuchMessageException e) {
message = defaultMessage;
}
@@ -84,7 +91,7 @@
public String getMessage(String key, Object[] args){
String message;
try {
- message = messageAccessor.getMessage(key,args,LocaleContextHolder.getLocale());
+ message = messageSource.getMessage(key,args,LocaleContextHolder.getLocale());
} catch (NoSuchMessageException e) {
message = "??" + key + "??";
}
@@ -100,7 +107,7 @@
public String getMessage(String key, Object[] args, String defaultMessage){
String message = defaultMessage;
try {
- message = messageAccessor.getMessage(key,args,defaultMessage,LocaleContextHolder.getLocale());
+ message = messageSource.getMessage(key,args,defaultMessage,LocaleContextHolder.getLocale());
} catch (NoSuchMessageException e) {
message = defaultMessage;
}