/*
* Copyright (C) 2011 Marius Giepz
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* 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.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package org.saiku.adhoc.service;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.pentaho.metadata.model.Domain;
import org.pentaho.metadata.model.LogicalColumn;
import org.pentaho.metadata.model.LogicalModel;
import org.pentaho.reporting.libraries.formula.Formula;
import org.pentaho.reporting.libraries.formula.parser.ParseException;
import org.saiku.adhoc.exceptions.SaikuAdhocException;
import org.saiku.adhoc.model.WorkspaceSessionHolder;
import org.saiku.adhoc.model.dto.DisplayName;
import org.saiku.adhoc.model.dto.ElementFormat;
import org.saiku.adhoc.model.dto.FilterValue;
import org.saiku.adhoc.model.dto.Position;
import org.saiku.adhoc.model.master.SaikuColumn;
import org.saiku.adhoc.model.master.SaikuGroup;
import org.saiku.adhoc.model.master.SaikuLabel;
import org.saiku.adhoc.model.master.SaikuMasterModel;
import org.saiku.adhoc.model.master.SaikuParameter;
import org.saiku.adhoc.model.master.SaikuReportSettings;
import org.saiku.adhoc.model.metadata.impl.MetadataModelInfo;
import org.saiku.adhoc.providers.IMetadataProvider;
import org.saiku.adhoc.utils.StringUtils;
import org.saiku.adhoc.utils.TemplateUtils;
/**
* @author mgiepz
*
*/
public class EditorService {
public IMetadataProvider getMetadataProvider() {
return metadataProvider;
}
public void setMetadataProvider(IMetadataProvider metadataProvider) {
this.metadataProvider = metadataProvider;
}
protected Log log = LogFactory.getLog(EditorService.class);
protected WorkspaceSessionHolder sessionHolder;
protected IMetadataProvider metadataProvider;
public void setSessionHolder(WorkspaceSessionHolder sessionHolder) {
this.sessionHolder = sessionHolder;
}
// private ReportGeneratorService reportGeneratorService;
//
// public void setReportGeneratorService(ReportGeneratorService reportGeneratorService) {
// this.reportGeneratorService = reportGeneratorService;
// }
public void createNewModel(String sessionId, MetadataModelInfo modelInfo) throws SaikuAdhocException {
SaikuMasterModel masterModel = null;
try {
if (modelInfo.getJson() == null) {
String domainId;
domainId = URLDecoder.decode(modelInfo.getDomainId(), "UTF-8");
Domain domain = metadataProvider.getDomain(domainId);
LogicalModel logicalModel = metadataProvider.getLogicalModel(domainId, modelInfo.getModelId());
masterModel = new SaikuMasterModel();
ModelHelper.init(masterModel, domain, logicalModel, sessionId);
} else {
ObjectMapper mapper = new ObjectMapper();
masterModel = mapper.readValue(modelInfo.getJson(), SaikuMasterModel.class);
String[] split = masterModel.getClientModelSelection().split("/");
String domainId = URLDecoder.decode(split[0], "UTF-8");
Domain domain = metadataProvider.getDomain(domainId);
LogicalModel logicalModel = metadataProvider.getLogicalModel(domainId, split[1]);
ModelHelper.init(masterModel, domain, logicalModel, sessionId);
masterModel.setCdaDirty(true);
}
sessionHolder.initSession(masterModel, sessionId);
sessionHolder.getModel(sessionId).setClientModelSelection(
URLEncoder.encode(masterModel.getDomainId(), "UTF-8") + "/"
+ masterModel.getLogicalModelId());
} catch (UnsupportedEncodingException e) {
final String message = e.getCause() != null ? e.getCause().getClass().getName() + " - "
+ e.getCause().getMessage() : e.getClass().getName() + " - " + e.getMessage();
log.error(message, e);
throw new SaikuAdhocException(message, e);
} catch (JsonParseException e) {
final String message = e.getCause() != null ? e.getCause().getClass().getName() + " - "
+ e.getCause().getMessage() : e.getClass().getName() + " - " + e.getMessage();
log.error(message, e);
throw new SaikuAdhocException(message, e);
} catch (JsonMappingException e) {
final String message = e.getCause() != null ? e.getCause().getClass().getName() + " - "
+ e.getCause().getMessage() : e.getClass().getName() + " - " + e.getMessage();
log.error(message, e);
throw new SaikuAdhocException(message, e);
} catch (IOException e) {
final String message = e.getCause() != null ? e.getCause().getClass().getName() + " - "
+ e.getCause().getMessage() : e.getClass().getName() + " - " + e.getMessage();
log.error(message, e);
throw new SaikuAdhocException(message, e);
}
if (log.isDebugEnabled()) {
log.debug("SERVICE:EditorService " + sessionId + " createNewModel\n" + sessionHolder.logModel(sessionId));
}
}
public DisplayName addColumn(String sessionId, String category, String columnId, Position position) {
final SaikuMasterModel model = sessionHolder.getModel(sessionId);
SaikuColumn column = findAndRemoveByUid(model, position.getUid());
List<SaikuColumn> columns = model.getColumns();
final LogicalModel logicalModel = metadataProvider.getLogicalModel(model.getDomainId(),model.getLogicalModelId());
LogicalColumn logicalColumn = logicalModel.findLogicalColumn(columnId);
if (column == null) {
column = new SaikuColumn(logicalColumn);
column.setName(StringUtils.getUniqueColumnName(column.getName(), columns));
column.setCategory(category);
column.setId(columnId);
column.setSelectedAggType(column.getDefaultAggType());
column.setSelectedSummaryType("NONE");
column.setUid(position.getUid());
}
columns.add(position.getPosition(), column);
model.setCdaDirty(true);
if (log.isDebugEnabled()) {
log.debug("SERVICE:EditorService " + sessionId + " addColumn\n" + sessionHolder.logModel(sessionId));
}
return new DisplayName(column.getName(), position.getUid());
}
/**
* This method is ambigeous. it removes all types of elements but only finds columns
*
* @param model
* @param uid
* @return
*/
private SaikuColumn findAndRemoveByUid(SaikuMasterModel model, String uid) {
final List<SaikuColumn> columns = model.getColumns();
SaikuColumn saikuColumn = null;
for (Iterator<SaikuColumn> iterator = columns.iterator(); iterator.hasNext();) {
SaikuColumn col = (SaikuColumn) iterator.next();
if (col.getUid().equals(uid)) {
saikuColumn = col;
break;
}
}
if (saikuColumn != null) {
columns.remove(saikuColumn);
return saikuColumn;
}
final List<SaikuGroup> groups = model.getGroups();
SaikuGroup saikuGroup = null;
for (Iterator<SaikuGroup> iterator2 = groups.iterator(); iterator2.hasNext();) {
SaikuGroup group = (SaikuGroup) iterator2.next();
if (group.getUid().equals(uid)) {
saikuGroup = group;
break;
}
}
if (saikuGroup != null) {
groups.remove(saikuGroup);
}
final List<SaikuParameter> params = model.getParameters();
SaikuParameter saikuParam = null;
for (Iterator<SaikuParameter> iterator3 = params.iterator(); iterator3.hasNext();) {
SaikuParameter param = (SaikuParameter) iterator3.next();
if (param.getUid().equals(uid)) {
saikuParam = param;
break;
}
}
if (saikuParam != null) {
params.remove(saikuParam);
}
return null;
/*
* Doesnt work with parameters yet
*
* final ArrayList<SaikuParameter> parameters = model.getParameters();
* for (SaikuParameter saikuParam : parameters) {
* if(saikuParam.g.equals(uid)){ groups.remove(saikuParam); } }
*/
}
public void removeColumn(String sessionId, String category, String businessColumn, Integer position) {
SaikuMasterModel model = sessionHolder.getModel(sessionId);
List<SaikuColumn> columns = model.getColumns();
int index = position;
columns.remove(index);
model.setCdaDirty(true);
if (log.isDebugEnabled()) {
log.debug("SERVICE:EditorService " + sessionId + " removeColumn\n" + sessionHolder.logModel(sessionId));
}
}
public void addFilter(String sessionId, String category, String businessColumn, Position position) {
final SaikuMasterModel model = sessionHolder.getModel(sessionId);
findAndRemoveByUid(model, position.getUid());
List<SaikuParameter> parameters = model.getParameters();
final LogicalModel logicalModel = metadataProvider.getLogicalModel(model.getDomainId(), model.getLogicalModelId());
LogicalColumn logicalColumn = logicalModel.findLogicalColumn(businessColumn);
SaikuParameter parameter = new SaikuParameter(logicalColumn);
parameter.setCategory(category);
parameter.setId(businessColumn);
parameter.setUid(position.getUid());
parameter.setType(logicalColumn.getDataType().getName());
parameters.add(position.getPosition(), parameter);
model.setCdaDirty(true);
if (log.isDebugEnabled()) {
log.debug("SERVICE:EditorService " + sessionId + " addFilter\n" + sessionHolder.logModel(sessionId));
}
}
public void removeFilter(String sessionId, String category, String businessColumn, int position) {
final SaikuMasterModel model = sessionHolder.getModel(sessionId);
List<SaikuParameter> parameters = model.getParameters();
parameters.remove(position);
//String filterKey = category + "." + businessColumn;
//model.getDerivedModels().getFilterQueries().remove(filterKey);
model.setCdaDirty(true);
if (log.isDebugEnabled()) {
log.debug("SERVICE:EditorService " + sessionId + " removeFilter\n" + sessionHolder.logModel(sessionId));
}
}
public void setFilterValues(String sessionId, String category, String businessColumn, ArrayList<FilterValue> selection) {
SaikuMasterModel model = sessionHolder.getModel(sessionId);
ArrayList<String> values = new ArrayList<String>();
for (FilterValue filterValue : selection) {
values.add(filterValue.getValue());
}
final List<SaikuParameter> parameters = model.getParameters();
for (SaikuParameter saikuParameter : parameters) {
if (saikuParameter.getCategory().equals(category) && saikuParameter.getId().equals(businessColumn)) {
saikuParameter.setParameterValues(values);
}
}
}
public void addGroup(String sessionId, String categoryId, String columnId, Position position) {
final SaikuMasterModel model = sessionHolder.getModel(sessionId);
findAndRemoveByUid(model, position.getUid());
String name = null;
List<SaikuGroup> groups = model.getGroups();
final LogicalModel logicalModel = metadataProvider.getLogicalModel(model.getDomainId(),model.getLogicalModelId());
LogicalColumn logicalColumn = logicalModel.findLogicalColumn(columnId);
if(logicalColumn!=null){
String locale = "en_En";
name = logicalColumn.getName(locale);
}
//if its not a logicalColumn it must be a calculated one
else{
final SaikuColumn column = ModelHelper.findColumnByUid(model, position.getUid());
name = column.getName();
}
SaikuGroup group = null;
// see if group is allready in the selection
for (SaikuGroup saikuGroup : groups) {
if (saikuGroup.getUid().equals(position.getUid())) {
group = saikuGroup;
}
}
if (group != null) {
groups.remove(group);
} else {
group = new SaikuGroup();
group.setColumnId(columnId);
group.setCategory(categoryId);
group.setColumnName(name);
group.setDisplayName(name);
group.setUid(position.getUid());
group.setGroupTotalsLabel("Total " + name);
}
groups.add(position.getPosition(), group);
model.setCdaDirty(true);
if (log.isDebugEnabled()) {
log.debug("SERVICE:EditorService " + sessionId + " addGroup\n" + sessionHolder.logModel(sessionId));
}
}
public void removeGroup(String sessionId, String category, String businessColumn, int position) {
SaikuMasterModel model = sessionHolder.getModel(sessionId);
List<SaikuGroup> groups = model.getGroups();
groups.remove(position);
model.setCdaDirty(true);
if (log.isDebugEnabled()) {
log.debug("SERVICE:EditorService " + sessionId + " removeGroup\n" + sessionHolder.logModel(sessionId));
}
}
public SaikuColumn getColumnConfig(String sessionId, String category, String column, Integer position) {
return sessionHolder.getModel(sessionId).getColumns().get(position);
}
public SaikuColumn getColumnConfig(String sessionId, String id) {
final List<SaikuColumn> columns = sessionHolder.getModel(sessionId).getColumns();
for (SaikuColumn saikuColumn : columns) {
if (id.equals(saikuColumn.getUid())) {
return saikuColumn;
}
}
return null;
}
public ElementFormat getElementFormat(String sessionId, String id) {
final SaikuMasterModel model = sessionHolder.getModel(sessionId);
if (id.contains("dtl")) {
final SaikuColumn column = ModelHelper.findColumnByLayoutId(model, id);
return new ElementFormat(column.getElementFormat().getTempFormat(), column.getName());
} else if (id.contains("dth")) {
final SaikuColumn column = ModelHelper.findColumnByLayoutId(model, id.replace("dth", "dtl"));
return new ElementFormat(column.getColumnHeaderFormat().getTempFormat(), column.getName());
} else if (id.contains("ghd")) {
final SaikuGroup saikuGroup = ModelHelper.findGroupById(model, id);
return new ElementFormat(saikuGroup.getGroupHeaderFormat().getTempFormat(), saikuGroup.getGroupName());
}
else if (id.contains("gft")) {
String[] splits = id.split("-");
Integer index = Integer.valueOf(splits[2]);
// get the correct group by index
SaikuGroup saikuGroup = model.getGroups().get(index);
final List<SaikuLabel> msgs = saikuGroup.getGroupFooterElements();
for (SaikuLabel msg : msgs) {
if (id.equals(msg.getUid())) {
return new ElementFormat(msg.getElementFormat().getTempFormat(), msg.getValue());
}
}
}
else if (id.contains("rhd")) {
final List<SaikuLabel> msgs = model.getReportHeaderElements();
for (SaikuLabel msg : msgs) {
if (id.equals(msg.getUid())) {
return new ElementFormat(msg.getElementFormat().getTempFormat(), msg.getValue());
}
}
} else if (id.contains("rft")) {
final List<SaikuLabel> msgs = model.getReportFooterElements();
for (SaikuLabel msg : msgs) {
if (id.equals(msg.getUid())) {
return new ElementFormat(msg.getElementFormat().getTempFormat(), msg.getValue());
}
}
} else if (id.contains("sum")) {
final List<SaikuLabel> msgs = model.getReportSummaryElements();
for (SaikuLabel msg : msgs) {
if (id.equals(msg.getUid())) {
return new ElementFormat(msg.getElementFormat().getTempFormat(), msg.getValue());
}
}
} else if (id.contains("phd")) {
final List<SaikuLabel> msgs = model.getPageHeaderElements();
for (SaikuLabel msg : msgs) {
if (id.equals(msg.getUid())) {
return new ElementFormat(msg.getElementFormat().getTempFormat(), msg.getValue());
}
}
} else if (id.contains("pft")) {
final List<SaikuLabel> msgs = model.getPageFooterElements();
for (SaikuLabel msg : msgs) {
if (id.equals(msg.getUid())) {
return new ElementFormat(msg.getElementFormat().getTempFormat(), msg.getValue());
}
}
}
return null;
}
public DisplayName setElementFormat(String sessionId, ElementFormat format, String id) throws Exception {
final SaikuMasterModel model = sessionHolder.getModel(sessionId);
String displayName = format.getValue();
if (id.contains("dtl")) {
final SaikuColumn saikuColumn = ModelHelper.findColumnByLayoutId(model, id);
TemplateUtils.mergeElementFormats(format.getFormat(), saikuColumn.getElementFormat());
}
else if (id.contains("dth")) {
final SaikuColumn m = ModelHelper.findColumnByLayoutId(model, id.replace("dth", "dtl"));
List<SaikuColumn> columns = model.getColumns();
if(format.getFormat().getWidth()==null){
format.getFormat().setWidth(m.getColumnHeaderFormat().getWidth());
}
TemplateUtils.mergeElementFormats(format.getFormat(), m.getColumnHeaderFormat());
if (!m.getName().equals(displayName)) {
m.setName(StringUtils.getUniqueColumnName(displayName, columns));
model.setCdaDirty(true);
}
return new DisplayName(m.getName(), m.getUid());
} else if (id.contains("ghd")) {
final SaikuGroup m = ModelHelper.findGroupById(model, id);
TemplateUtils.mergeElementFormats(format.getFormat(), m.getGroupHeaderFormat());
m.setGroupName(displayName);
} else if (id.contains("gft") || id.contains("rhd")) {
final SaikuLabel m = ModelHelper.findLabelById(model, id);
TemplateUtils.mergeElementFormats(format.getFormat(), m.getElementFormat());
m.setValue(displayName);
} else if (id.contains("sum")) {
final List<SaikuLabel> msgs = model.getReportSummaryElements();
setFormat(format, id, msgs);
} else if (id.contains("rft")) {
final List<SaikuLabel> msgs = model.getReportFooterElements();
setFormat(format, id, msgs);
} else if (id.contains("phd")) {
final List<SaikuLabel> msgs = model.getPageHeaderElements();
setFormat(format, id, msgs);
} else if (id.contains("pft")) {
final List<SaikuLabel> msgs = model.getPageFooterElements();
setFormat(format, id, msgs);
}
return null;
}
private void setFormat(ElementFormat format, String id, final List<SaikuLabel> msgs) throws Exception {
for (SaikuLabel msg : msgs) {
if (id.equals(msg.getUid())) {
TemplateUtils.mergeElementFormats(format.getFormat(), msg.getElementFormat());
msg.setValue(format.getValue());
}
}
}
public DisplayName setColumnConfig(String sessionId, String category, String businessColumn, Integer position,
SaikuColumn config) throws ParseException {
SaikuMasterModel model = sessionHolder.getModel(sessionId);
List<SaikuColumn> columns = model.getColumns();
if(config.getFormula()!=null){
final Formula f = new Formula(config.getFormula());
}
// The new name must be unique among the columns (excluding itself);
List<String> colNames = new ArrayList<String>();
for (SaikuColumn col : columns) {
if (columns.indexOf(col) != position) {
colNames.add(col.getName());
}
}
String newName = StringUtils.getUniqueName(config.getName(), colNames);
config.setName(newName);
model.setCdaDirty(true);
if (category.equals("CALCULATED") && config.getId().equals("NEW")) {
config.setId(UUID.randomUUID().toString());
columns.add(config);
} else {
SaikuColumn oldCol = columns.set(position, config);
// if we changed the name we need to replace it in all formulas
String oldName = oldCol.getName();
if (!oldName.equals(newName)) {
replaceInFormulas(oldName, newName, columns);
}
}
return new DisplayName(config.getName(), config.getUid());
}
private void replaceInFormulas(String oldName, String newName, List<SaikuColumn> columns) {
String tokenOld = "\\[" + oldName + "\\]";
String tokenNew = "[" + newName + "]";
for (SaikuColumn saikuColumn : columns) {
String formula = saikuColumn.getFormula();
if (formula != null)
saikuColumn.setFormula(formula.replaceAll(tokenOld, tokenNew));
}
}
public void setColumnSort(String sessionId, String category, String column, Integer position, String order) {
SaikuMasterModel model = sessionHolder.getModel(sessionId);
model.getColumns().get(position).setSort(order);
model.setCdaDirty(true);
}
public void setGroupSort(String sessionId, String category, String column, Integer position, String order) {
SaikuMasterModel model = sessionHolder.getModel(sessionId);
model.getGroups().get(position).setSort(order);
model.setCdaDirty(true);
}
public String getModelJson(String sessionId) throws JsonGenerationException, JsonMappingException, IOException {
String value = null;
SaikuMasterModel model = sessionHolder.getModel(sessionId);
ObjectMapper mapper = new ObjectMapper();
value = mapper.writeValueAsString(model);
log.debug(value);
return value;
}
public void setRowlimit(String sessionId, String rowlimit) {
SaikuMasterModel model = sessionHolder.getModel(sessionId);
model.getSettings().setLimit(Integer.parseInt(rowlimit));
model.setCdaDirty(true);
}
public void setDistinct(String sessionId, Boolean distinct) {
SaikuMasterModel model = sessionHolder.getModel(sessionId);
model.getSettings().setDisableDistinct(distinct);
model.setCdaDirty(true);
}
public SaikuReportSettings getReportSettings(String sessionId) {
return sessionHolder.getModel(sessionId).getSettings();
}
public void setReportSettings(String sessionId, SaikuReportSettings settings) {
sessionHolder.getModel(sessionId).setSettings(settings);
}
}