/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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 Lesser General Public License for more details.
*
* Copyright (c) 2011 Pentaho Corporation. All rights reserved.
*
* Created Jan, 2011
* @author jdixon
*/
package org.pentaho.platform.dataaccess.metadata.service;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.pentaho.commons.connection.IPentahoResultSet;
import org.pentaho.metadata.model.Domain;
import org.pentaho.metadata.model.IPhysicalColumn;
import org.pentaho.metadata.model.LogicalColumn;
import org.pentaho.metadata.model.LogicalModel;
import org.pentaho.metadata.model.concept.Concept;
import org.pentaho.metadata.model.concept.types.AggregationType;
import org.pentaho.metadata.model.concept.types.Alignment;
import org.pentaho.metadata.model.concept.types.DataType;
import org.pentaho.metadata.model.concept.types.FieldType;
import org.pentaho.metadata.model.concept.types.LocalizedString;
import org.pentaho.metadata.query.model.CombinationType;
import org.pentaho.metadata.query.model.Constraint;
import org.pentaho.metadata.query.model.Selection;
import org.pentaho.metadata.query.model.Order.Type;
import org.pentaho.metadata.query.model.util.QueryXmlHelper;
import org.pentaho.metadata.repository.IMetadataDomainRepository;
import org.pentaho.platform.dataaccess.metadata.messages.Messages;
import org.pentaho.platform.dataaccess.metadata.model.impl.Category;
import org.pentaho.platform.dataaccess.metadata.model.impl.Column;
import org.pentaho.platform.dataaccess.metadata.model.impl.Condition;
import org.pentaho.platform.dataaccess.metadata.model.impl.Model;
import org.pentaho.platform.dataaccess.metadata.model.impl.Order;
import org.pentaho.platform.dataaccess.metadata.model.impl.Parameter;
import org.pentaho.platform.dataaccess.metadata.model.impl.Query;
import org.pentaho.platform.engine.core.system.PentahoBase;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.util.messages.LocaleHelper;
import org.pentaho.pms.core.exception.PentahoMetadataException;
import flexjson.JSONDeserializer;
/**
* This class provides utility functions used by the MetadataService
* @author jamesdixon
*
*/
@SuppressWarnings({"deprecation"})
public class MetadataServiceUtil extends PentahoBase {
private static final long serialVersionUID = -123835493828427853L;
private Log logger = LogFactory.getLog(MetadataServiceUtil.class);
private org.pentaho.metadata.model.Domain domain;
/**
* Returns the full domain object used by this class
* @return
*/
public org.pentaho.metadata.model.Domain getDomain() {
return domain;
}
/**
* Sets the full domain object used by this class
* @param domain
*/
public void setDomain(org.pentaho.metadata.model.Domain domain) {
this.domain = domain;
}
public MetadataServiceUtil() {
}
/**
* Works out what is the most appropriate locale to use given a domain and the user's
* current locale
* @return
*/
protected String getLocale( ) {
String locale = LocaleHelper.getClosestLocale(LocaleHelper.getLocale().toString(), domain.getLocaleCodes());
return locale;
}
/**
* Creates a lightweight, serializable model object from a logical model
* @param m
* @param domainId
* @return
*/
public Model createThinModel(LogicalModel m, String domainId) {
// create the model object
Model model = new Model();
model.setName(m.getName(getLocale()));
model.setId(m.getId());
model.setDomainId(domainId);
model.setDescription(m.getDescription(getLocale()));
// add the categories to the model
List<Category> categories = new ArrayList<Category>();
for (org.pentaho.metadata.model.Category cat : m.getCategories()) {
categories.add(createCategory(m, cat));
}
model.setCategories(categories.toArray(new Category[categories.size()]));
return model;
}
/**
* Creates a lightweight, serializable category objects from a logical model category
* @param m
* @param c
* @return
*/
private Category createCategory(LogicalModel m, org.pentaho.metadata.model.Category c) {
// create a thin category object
Category cat = new Category();
cat.setName(c.getName(getLocale()));
cat.setId(c.getId());
List<Column> columns = new ArrayList<Column>();
for (LogicalColumn col : c.getLogicalColumns()) {
columns.add(createColumn(m, col, c));
}
cat.setColumns(columns.toArray(new Column[columns.size()]));
return cat;
}
/**
* Creates a lightweight, serializable Column object from a logical model column
* @param m
* @param c
* @return
*/
private Column createColumn(LogicalModel m, LogicalColumn c, org.pentaho.metadata.model.Category category) {
Column col = new Column();
col.setName(c.getName(getLocale()));
col.setId(c.getId());
if( c.getFieldType() != null ) {
col.setFieldType(c.getFieldType().name());
} else {
col.setFieldType( "UNKNOWN" ); //$NON-NLS-1$
}
col.setType(c.getDataType().getName().toUpperCase());
col.setCategory(category.getId());
// set the aggregation fields for the column
List<AggregationType> possibleAggs = c.getAggregationList();
List<String> aggTypes = new ArrayList<String>();
if (possibleAggs != null) {
for (AggregationType agg : possibleAggs) {
aggTypes.add(agg.name());
}
}
// There might be a default agg, but no agg list. If so, add it to the list.
AggregationType defaultAggType = AggregationType.NONE;
if (c.getAggregationType() != null) {
defaultAggType = c.getAggregationType();
}
if (!aggTypes.contains(defaultAggType)) {
aggTypes.add(defaultAggType.name());
}
col.setAggTypes(aggTypes);
col.setDefaultAggType(defaultAggType.name());
col.setSelectedAggType(defaultAggType.name());
// set the alignment
DataType dataType = c.getDataType();
FieldType fieldType = c.getFieldType();
Object obj = c.getProperty("alignment"); //$NON-NLS-1$
if(obj instanceof Alignment) {
if(obj == Alignment.LEFT) {
col.setHorizontalAlignment(Alignment.LEFT.toString());
}
else if(obj == Alignment.RIGHT) {
col.setHorizontalAlignment(Alignment.RIGHT.toString());
}
else if(obj == Alignment.CENTERED) {
col.setHorizontalAlignment(Alignment.CENTERED.toString());
}
}
else if(fieldType == FieldType.FACT) {
col.setHorizontalAlignment(Alignment.RIGHT.toString());
}
else if(fieldType == FieldType.OTHER && dataType == DataType.NUMERIC) {
col.setHorizontalAlignment(Alignment.RIGHT.toString());
}
else {
col.setHorizontalAlignment(Alignment.LEFT.toString());
}
// set the format mask
obj = c.getProperty("mask"); //$NON-NLS-1$
if(obj != null) {
col.setFormatMask((String)obj);
}
return col;
}
/**
* Returns a CDA JSON representation of a query result set
* @param resultSet
* @return
* @throws JSONException
*/
public String createCdaJson(final IPentahoResultSet resultSet, String locale) throws JSONException
{
if(resultSet == null) {
return null;
}
JSONObject json = new JSONObject();
// Generate the metadata
final JSONArray metadataArray = new JSONArray();
final int columnCount = resultSet.getColumnCount();
final int rowCount = resultSet.getRowCount();
for (int i = 0; i < columnCount; i++)
{
JSONObject info = new JSONObject();
info.put("colIndex", i); //$NON-NLS-1$
info.put("colName", resultSet.getMetaData().getColumnHeaders()[0][i]); //$NON-NLS-1$
DataType type = (DataType) resultSet.getMetaData().getAttribute(0, i, IPhysicalColumn.DATATYPE_PROPERTY);
info.put("colType", type.getName().toUpperCase()); //$NON-NLS-1$
LocalizedString name = (LocalizedString) resultSet.getMetaData().getAttribute(0, i, Concept.NAME_PROPERTY);
if( name != null && locale!=null) {
info.put("colLabel", name.getString(locale)); //$NON-NLS-1$
}
metadataArray.put(info);
}
json.put("metadata", metadataArray); //$NON-NLS-1$
// add the rows of data
final JSONArray valuesArray = new JSONArray();
for (int rowIdx = 0; rowIdx < rowCount; rowIdx++)
{
final JSONArray rowArray = new JSONArray();
valuesArray.put(rowArray);
for (int colIdx = 0; colIdx < columnCount; colIdx++)
{
rowArray.put(resultSet.getValueAt(rowIdx,colIdx));
}
}
json.put("resultset", valuesArray); //$NON-NLS-1$
return json.toString();
}
/**
* Returns the full domain obejct for a XML MQL query
* @param query
* @return
* @throws PentahoMetadataException
*/
public org.pentaho.metadata.model.Domain getDomainObject( String query ) throws PentahoMetadataException {
QueryXmlHelper helper = new QueryXmlHelper();
IMetadataDomainRepository domainRepository = PentahoSystem.get(IMetadataDomainRepository.class, PentahoSessionHolder.getSession());
org.pentaho.metadata.query.model.Query fatQuery = helper.fromXML(domainRepository, query);
return fatQuery.getDomain();
}
/**
* Converts a thin query model into a full query
* @param src
* @return
*/
public org.pentaho.metadata.query.model.Query convertQuery(Query src) {
IMetadataDomainRepository domainRepository = PentahoSystem.get(IMetadataDomainRepository.class, PentahoSessionHolder.getSession());
Domain fullDomain = domainRepository.getDomain( src.getDomainName() );
LogicalModel logicalModel = fullDomain.findLogicalModel(src.getModelId());
// create a new full query object
org.pentaho.metadata.query.model.Query dest = new org.pentaho.metadata.query.model.Query(fullDomain, logicalModel);
// now add the selections
List<Selection> selections = dest.getSelections();
for( Column column : src.getColumns() ) {
// get the objects needed for the selection
LogicalColumn logicalColumn = logicalModel.findLogicalColumn(column.getId());
org.pentaho.metadata.model.Category category = getCategory( column.getId(), logicalModel );
AggregationType aggregationType = AggregationType.valueOf(column.getSelectedAggType());
// create a selection and add it to the list
Selection selection = new Selection(category, logicalColumn, aggregationType);
selections.add(selection);
}
// now add the filters
List<Constraint> constraints = dest.getConstraints();
for(Condition condition : src.getConditions()) {
org.pentaho.metadata.query.model.CombinationType combinationType = CombinationType.valueOf(condition.getCombinationType());
LogicalColumn logicalColumn = logicalModel.findLogicalColumn(condition.getColumn());
String paramName = null;
for(Parameter parameter : src.getParameters()) {
if(parameter.getColumn().equals(condition.getColumn())) {
paramName = parameter.getName() == null ? parameter.getColumn() : parameter.getName();
}
}
// condition.setParameterized(parameterized);
String formula = condition.getCondition(logicalColumn.getDataType().name(), paramName);
Constraint constraint = new Constraint(combinationType, formula);
constraints.add(constraint);
}
// now set the disable distinct option
if( src.getDisableDistinct() != null ) {
dest.setDisableDistinct(src.getDisableDistinct());
}
// now add the sorting information
List<org.pentaho.metadata.query.model.Order> orders = dest.getOrders();
for(Order order : src.getOrders()) {
// find the selection
for(Selection selection : selections) {
if(selection.getLogicalColumn().getId().equals(order.getColumn() ) ) {
Type type = Type.valueOf(order.getOrderType());
org.pentaho.metadata.query.model.Order fullOrder = new org.pentaho.metadata.query.model.Order(selection, type);
orders.add(fullOrder);
}
}
}
// now add the parameter information
List<org.pentaho.metadata.query.model.Parameter> parameters = dest.getParameters();
for(Parameter parameter : src.getParameters()) {
// find the column for this parameter
LogicalColumn logicalColumn = logicalModel.findLogicalColumn(parameter.getColumn());
DataType type = logicalColumn.getDataType();
String value[] = parameter.getValue();
org.pentaho.metadata.query.model.Parameter fullParam = new org.pentaho.metadata.query.model.Parameter( parameter.getColumn(), type, value[0]);
parameters.add(fullParam);
}
return dest;
}
/**
* Returns the full category object for a given column within the logical model
* @param columnId
* @param logicalModel
* @return
*/
protected org.pentaho.metadata.model.Category getCategory( String columnId, LogicalModel logicalModel ) {
for( org.pentaho.metadata.model.Category category : logicalModel.getCategories() ) {
if( category.findLogicalColumn(columnId) != null) {
return category;
}
}
return null;
}
/**
* Deserializes a JSON query into a thin Query object
* @param json
* @return
*/
public Query deserializeJsonQuery(String json) {
try {
// convert the json query into a thin query model
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
return new JSONDeserializer<Query>().deserialize( json );
} finally {
Thread.currentThread().setContextClassLoader(oldLoader);
}
} catch (Exception e) {
error(Messages.getErrorString("MetadataService.ERROR_0007_BAD_JSON", json), e ); //$NON-NLS-1$
return null;
}
}
@Override
public Log getLogger() {
return logger;
}
}