/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
* <p>
*/
package org.olat.core.logging.activity;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
/**
* A ResourceableTypeList is a structured collection of
* ILoggingResourceableTypes specifying which of them are mandatory with a
* LoggingAction and which are optional.
* <p>
* The idea of ResouceableTypeList is to be able to make checks between
* the businessPath and
* all the LoggingResourceables collected during the Controller lifetime,
* the event handling and information passed with the log() call.
* <p>
* Check with the LoggingAction class to see most common use cases
* of ResourceableTypeList - here's an excerpt though:<br>
* <pre>
* new ResourceableTypeList().
* addMandatory(OlatResourceableType.wiki).
* or().addMandatory(OlatResourceableType.businessGroup).addOptional(OlatResourceableType.wiki);
* </pre>
* So the idea is to chain addMandatory and addOptional calls for as long
* as types need to be added to the same list - then OR that by calling or()
* and add a second/third variant which also is allowed etc etc.
* As soon as one of the lists validates to true it will return true for the whole list.
* <P>
* Initial Date: 21.10.2009 <br>
* @author Stefan
*/
public class ResourceableTypeList {
private OLog logger = Tracing.createLoggerFor(this.getClass());
/** list of mandatory ILoggingResourceableTypes **/
private List<ILoggingResourceableType> mandatory_;
/** list of optional ILoggingResourceableTypes **/
private List<ILoggingResourceableType> optional_;
/** a general magic 'allow anything' flag **/
private boolean allowAnything_ = false;
/** list of ORed sub-ResourceableTypeLists - this is only set on the parent (the top most list) **/
private List<ResourceableTypeList> orList_;
/** ORed lists are stored in the parent_'s orList_ - also, if this list has a parent
* it will always go to the parent to do a job through the whole list/OR structure
*/
private ResourceableTypeList parent_ = null;
/**
* Create an empty ResourceableTypeList.
* <p>
* Call this to later add ILoggingResourceableTypes using addMandatory() and addOptional().
* <p>
* When a new ORed sublist needs to be created, this can be done via the convenient or() method.
*/
public ResourceableTypeList() {
// ok
}
/**
* Starts a new ResourceableTypeList which is ORed with the callee list
* @return a new ReosurceableTypeList which is ORed with the callee list
*/
public ResourceableTypeList or() {
if (parent_!=null) {
return parent_.or();
}
ResourceableTypeList result = new ResourceableTypeList();
result.parent_ = this;
getOrs().add(result);
return result;
}
/**
* Add any number of ILoggingResourceableTypes as <b>mandatory</b> to this list.
* <p>
* Note that addMandatory() and addOptional() methods can be chained/repeated for as often as required
* @param resourceableTypes the list of ILoggingResourceableTypes to be added as mandatory to this list
* @return this
*/
public ResourceableTypeList addMandatory(ILoggingResourceableType... resourceableTypes) {
for (int i = 0; i < resourceableTypes.length; i++) {
ILoggingResourceableType loggingResourceableType = resourceableTypes[i];
getMandatory().add(loggingResourceableType);
}
return this;
}
/**
* Add any number of ILoggingResourceableTypes as <b>optional</b> to this list.
* <p>
* Note that addMandatory() and addOptional() methods can be chained/repeated for as often as required
* @param resourceableTypes the list of ILoggingResourceableTypes to be added as optional to this list
* @return this
*/
public ResourceableTypeList addOptional(ILoggingResourceableType... resourceableTypes) {
for (int i = 0; i < resourceableTypes.length; i++) {
ILoggingResourceableType loggingResourceableType = resourceableTypes[i];
getOptional().add(loggingResourceableType);
}
return this;
}
/**
* A magic-stick kind of 'allow everything' used to create a ResourceableTypeList which
* doesn't do any checks such as mandatory/optional/or etc at all but simply allows anything
* and everything.
*/
public void allowAnything() {
allowAnything_ = true;
// note that this method doesn't return the ResourceableTypeList as the other 'addOptional/addMandatory' methods
// do - this is because after allowAny you don't need to add anything further - since allowAny will cause
// the check to return true in all cases
}
@Override
public String toString() {
if (parent_!=null) {
return parent_.toString();
}
StringBuffer sb = new StringBuffer(thisAsString());
if (orList_!=null && orList_.size()>0) {
for (Iterator<ResourceableTypeList> it = orList_.iterator(); it.hasNext();) {
ResourceableTypeList list = it.next();
sb.append(" OR ");
sb.append(list.thisAsString());
}
}
return sb.toString();
}
/**
* Part of toString() used to output orList children
* @return
*/
private String thisAsString() {
StringBuffer sb = new StringBuffer();
sb.append("[");
if (getMandatory().size()>0) {
sb.append("Mandatory are ");
for (Iterator<ILoggingResourceableType> it = getMandatory().iterator(); it.hasNext();) {
ILoggingResourceableType mandatoryType = it.next();
sb.append(mandatoryType.name());
if (it.hasNext()) {
sb.append(",");
}
}
sb.append(". ");
}
if (getOptional().size()>0) {
sb.append("Optional are ");
for (Iterator<ILoggingResourceableType> it = getOptional().iterator(); it.hasNext();) {
ILoggingResourceableType optionalType = it.next();
sb.append(optionalType.name());
if (it.hasNext()) {
sb.append(",");
}
}
sb.append(".");
}
sb.append("]");
return sb.toString();
}
/**
* internal getter for the OR list - initializes the orList_ if it's null
* @return the orList_ - never null
*/
private List<ResourceableTypeList> getOrs() {
if (orList_==null) {
orList_ = new LinkedList<ResourceableTypeList>();
}
return orList_;
}
/**
* internal getter for the mandatory list - initializes mandatory_ if it's null
* @return the mandatory_ List - never null
*/
private List<ILoggingResourceableType> getMandatory() {
if (mandatory_==null) {
mandatory_ = new LinkedList<ILoggingResourceableType>();
}
return mandatory_;
}
/**
* internal getter for the optional list - initializes optional_ if it's null
* @return the optional_ List - never null
*/
private List<ILoggingResourceableType> getOptional() {
if (optional_==null) {
optional_ = new LinkedList<ILoggingResourceableType>();
}
return optional_;
}
/**
* Goes through the resourceInfos list and returns the LoggingResourceable which has the same type
* as the passed ILoggingResourceableType.
* @param resourceInfos the list which should be searched through
* @param type the type which we are looking for
* @return the LoggingResourceable matching the given type - or null
*/
private ILoggingResourceable findLoggingResourceInfo(List<ILoggingResourceable> resourceInfos, ILoggingResourceableType type) {
if (resourceInfos==null) {
throw new IllegalArgumentException("resourceInfos must not be null");
}
if (type==null) {
throw new IllegalArgumentException("type must not be null");
}
for (Iterator<ILoggingResourceable> it = resourceInfos.iterator(); it.hasNext();) {
ILoggingResourceable loggingResourceInfo = it.next();
if (loggingResourceInfo!=null && loggingResourceInfo.getResourceableType()==type) {
return loggingResourceInfo;
}
}
return null;
}
/**
* Executes the businessPath-check on <b>this</b> List - i.e. not on any parent
* or orList_ child but exactly on this list.
* <p>
* This method is called by executeCheckAndGetErrorMessage() which deals with
* parent/orList tweaks.
* @param resourceInfos the list which should be checked against
* @return null if we have a match, the error message otherwise
*/
private String doExecuteCheckAndGetErrorMessage(List<ILoggingResourceable> resourceInfos) {
if (allowAnything_) {
// that's the jumbo "everything ok" kind of resource type list
return null;
}
List<ILoggingResourceable> resourceInfosCopy = new LinkedList<ILoggingResourceable>(resourceInfos);
for (Iterator<ILoggingResourceable> it = resourceInfosCopy.iterator(); it.hasNext();) {
if(it.next().isIgnorable()) {
it.remove();
}
}
List<ILoggingResourceableType> mandatory = getMandatory();
for (Iterator<ILoggingResourceableType> it = mandatory.iterator(); it.hasNext();) {
ILoggingResourceableType type = it.next();
if ( isAllCondition(type) ) {
return checkAllCondition(it, resourceInfos);
}
ILoggingResourceable lri = findLoggingResourceInfo(resourceInfos, type);
if (lri==null) {
// error - return the error msg to reflect this
return "Mandatory resource not available: "+type.name();
} else {
resourceInfosCopy.remove(lri);
}
}
List<ILoggingResourceableType> optional = getOptional();
for (Iterator<ILoggingResourceable> it = resourceInfosCopy.iterator(); it.hasNext();) {
ILoggingResourceable lri = it.next();
final ILoggingResourceableType type = lri.getResourceableType();
if (type!=null && !optional.contains(type)) {
// error - return the error msg to reflect this
return "Resource set which is neither mandatory nor optional: "+type+", name="+lri.getName();
}
}
// everything ok - return null to reflect this
return null;
}
/**
* Check if type is a 'all' (*) condition.
* @param type
* @return
*/
private boolean isAllCondition(ILoggingResourceableType type) {
return type == StringResourceableType.anyBefore;
}
/**
* Special case for condition [all (*)] [type]
* @param currentIterator
* @param resourceInfos
* @return
*/
private String checkAllCondition(Iterator<ILoggingResourceableType> currentIterator, List<ILoggingResourceable> resourceInfos) {
logger.debug("special case *.type");
if (currentIterator.hasNext()) {
ILoggingResourceableType subtype = currentIterator.next();
logger.debug("subtype=" + subtype);
ILoggingResourceable lri = findLoggingResourceInfo(resourceInfos, subtype);
if (lri==null) {
return "Mandatory resource not available: "+subtype.name();
} else {
logger.debug("ok an finish" );
return null;
}
} else {
return "'all' without a type not allowed";
}
}
/**
* Executes the businessPath check on this list - this includes taking into account
* any or()-ed sublists.
* <p>
* @param resourceInfos the list to be checked against
* @return null if we have a match, the error message otherwise
*/
public String executeCheckAndGetErrorMessage(List<ILoggingResourceable> resourceInfos) {
if (parent_!=null) {
return parent_.executeCheckAndGetErrorMessage(resourceInfos);
}
// we are at the parent
String result = doExecuteCheckAndGetErrorMessage(resourceInfos);
if (result==null) {
// fine
return null;
}
for (Iterator<ResourceableTypeList> it = getOrs().iterator(); it.hasNext();) {
ResourceableTypeList list = it.next();
String orResult = list.doExecuteCheckAndGetErrorMessage(resourceInfos);
if (orResult==null) {
// fine
return null;
}
}
return result;
}
}