Java tutorial
/*! ****************************************************************************** * * Pentaho Community Edition Project: pentaho-modeler * * Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com * * ******************************************************************************* * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 License for the specific language governing permissions and * limitations under the License. * ********************************************************************************/ package org.pentaho.agilebi.modeler.models.annotations; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.EqualsBuilder; import org.pentaho.agilebi.modeler.ModelerException; import org.pentaho.agilebi.modeler.ModelerWorkspace; import org.pentaho.agilebi.modeler.geo.GeoContext; import org.pentaho.agilebi.modeler.geo.GeoRole; import org.pentaho.agilebi.modeler.models.annotations.data.InlineFormatAnnotation; import org.pentaho.agilebi.modeler.nodes.DimensionMetaData; import org.pentaho.agilebi.modeler.nodes.DimensionMetaDataCollection; import org.pentaho.agilebi.modeler.nodes.HierarchyMetaData; import org.pentaho.agilebi.modeler.nodes.LevelMetaData; import org.pentaho.agilebi.modeler.nodes.TimeRole; import org.pentaho.di.core.Const; import org.pentaho.di.core.injection.Injection; import org.pentaho.di.i18n.BaseMessages; import org.pentaho.metadata.model.LogicalColumn; import org.pentaho.metadata.model.olap.OlapDimension; import org.pentaho.metastore.api.IMetaStore; import org.pentaho.metastore.persist.MetaStoreAttribute; import org.w3c.dom.Document; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.logging.Logger; /** * @author Rowell Belen */ public class CreateAttribute extends AnnotationType { private static final long serialVersionUID = 5169827225345800226L; public static final String MDI_GROUP = "ATTRIBUTE"; private static transient Logger logger = Logger.getLogger(AnnotationType.class.getName()); private static final String DEFAULT_AUTO_GEO_DIMENSION_NAME = "Geography"; public static final String NAME_ID = "name"; public static final String NAME_NAME = "Attribute Name"; public static final int NAME_ORDER = 0; public static final String TIME_TYPE_ID = "timeType"; public static final String TIME_TYPE_NAME = "Time Level Type"; public static final int TIME_TYPE_ORDER = 1; public static final String TIME_FORMAT_ID = "timeFormat"; public static final String TIME_FORMAT_NAME = "Time Source Format"; public static final int TIME_FORMAT_ORDER = 2; public static final String GEO_TYPE_ID = "geoType"; public static final String GEO_TYPE_NAME = "Geo Type"; public static final int GEO_TYPE_ORDER = 3; public static final String ORDINAL_FIELD_ID = "ordinalField"; public static final String ORDINAL_FIELD_NAME = "Ordinal Field"; public static final int ORDINAL_FIELD_ORDER = 4; public static final String FORMAT_STRING_ID = "formatString"; public static final String FORMAT_STRING_NAME = "Format"; public static final int FORMAT_STRING_ORDER = 5; public static final String DESCRIPTION_ID = "description"; public static final String DESCRIPTION_NAME = "Description"; public static final int DESCRIPTION_ORDER = 6; public static final String BUSINESS_GROUP_ID = "businessGroup"; public static final String BUSINESS_GROUP_NAME = "Business Group"; public static final int BUSINESS_GROUP_ORDER = 7; public static final String PARENT_ATTRIBUTE_ID = "parentAttribute"; public static final String PARENT_ATTRIBUTE_NAME = "Parent Attribute"; public static final int PARENT_ATTRIBUTE_ORDER = 8; public static final String DIMENSION_ID = "dimension"; public static final String DIMENSION_NAME = "Dimension"; public static final int DIMENSION_ORDER = 9; public static final String HIERARCHY_ID = "hierarchy"; public static final String HIERARCHY_NAME = "Hierarchy"; public static final int HIERARCHY_ORDER = 10; public static final String UNIQUE_ID = "unique"; public static final String UNIQUE_NAME = "Is Unique"; public static final int UNIQUE_ORDER = 11; public static final String FIELD_ID = "field"; public static final String FIELD_NAME = "Field"; public static final int FIELD_ORDER = 12; public static final String LEVEL_ID = "level"; public static final String LEVEL_NAME = "Level"; public static final int LEVEL_ORDER = 13; public static final String CUBE_ID = "cube"; public static final String CUBE_NAME = "Cube"; public static final int CUBE_ORDER = 14; public static final String HIDDEN_ID = "hidden"; public static final String HIDDEN_NAME = "Hidden"; public static final int HIDDEN_ORDER = 15; @MetaStoreAttribute @ModelProperty(id = NAME_ID, name = NAME_NAME, order = NAME_ORDER) @Injection(name = MDI_GROUP + "_NAME", group = MDI_GROUP) private String name; @MetaStoreAttribute @ModelProperty(id = UNIQUE_ID, name = UNIQUE_NAME, order = UNIQUE_ORDER) @Injection(name = MDI_GROUP + "_IS_UNIQUE", group = MDI_GROUP) private boolean unique; @MetaStoreAttribute @ModelProperty(id = TIME_FORMAT_ID, name = TIME_FORMAT_NAME, order = TIME_FORMAT_ORDER) @Injection(name = MDI_GROUP + "_TIME_FORMAT", group = MDI_GROUP) private String timeFormat; @MetaStoreAttribute @ModelProperty(id = TIME_TYPE_ID, name = TIME_TYPE_NAME, order = TIME_TYPE_ORDER) @Injection(name = MDI_GROUP + "_TIME_TYPE", group = MDI_GROUP) private ModelAnnotation.TimeType timeType; @MetaStoreAttribute @ModelProperty(id = GEO_TYPE_ID, name = GEO_TYPE_NAME, order = GEO_TYPE_ORDER) @Injection(name = MDI_GROUP + "_GEO_TYPE", group = MDI_GROUP) private ModelAnnotation.GeoType geoType; @MetaStoreAttribute @ModelProperty(id = ORDINAL_FIELD_ID, name = ORDINAL_FIELD_NAME, order = ORDINAL_FIELD_ORDER) @Injection(name = MDI_GROUP + "_ORDINAL_FIELD", group = MDI_GROUP) private String ordinalField; @MetaStoreAttribute @ModelProperty(id = FORMAT_STRING_ID, name = FORMAT_STRING_NAME, order = FORMAT_STRING_ORDER, appliesTo = { ModelProperty.AppliesTo.Numeric, ModelProperty.AppliesTo.Time }) @Injection(name = MDI_GROUP + "_FORMAT_STRING", group = MDI_GROUP) private String formatString; @MetaStoreAttribute @ModelProperty(id = PARENT_ATTRIBUTE_ID, name = PARENT_ATTRIBUTE_NAME, order = PARENT_ATTRIBUTE_ORDER) @Injection(name = MDI_GROUP + "_PARENT", group = MDI_GROUP) private String parentAttribute; @MetaStoreAttribute @ModelProperty(id = DIMENSION_ID, name = DIMENSION_NAME, order = DIMENSION_ORDER) @Injection(name = MDI_GROUP + "_DIMENSION", group = MDI_GROUP) private String dimension; @MetaStoreAttribute @ModelProperty(id = HIERARCHY_ID, name = HIERARCHY_NAME, order = HIERARCHY_ORDER) @Injection(name = MDI_GROUP + "_HIERARCHY", group = MDI_GROUP) private String hierarchy; @MetaStoreAttribute @ModelProperty(id = DESCRIPTION_ID, name = DESCRIPTION_NAME, order = DESCRIPTION_ORDER) @Injection(name = MDI_GROUP + "_DESCRIPTION", group = MDI_GROUP) private String description; @MetaStoreAttribute // Do not expose business group in the UI (for now) //@ModelProperty( id = BUSINESS_GROUP_ID, name = BUSINESS_GROUP_NAME, order = BUSINESS_GROUP_ORDER ) private String businessGroup; @MetaStoreAttribute @ModelProperty(id = FIELD_ID, name = FIELD_NAME, order = FIELD_ORDER, hideUI = true) @Injection(name = MDI_GROUP + "_FIELD", group = MDI_GROUP) private String field; @MetaStoreAttribute @ModelProperty(id = LEVEL_ID, name = LEVEL_NAME, order = LEVEL_ORDER, hideUI = true) private String level; @MetaStoreAttribute @ModelProperty(id = CUBE_ID, name = CUBE_NAME, order = CUBE_ORDER, hideUI = true) private String cube; @MetaStoreAttribute @ModelProperty(id = HIDDEN_ID, name = HIDDEN_NAME, order = HIDDEN_ORDER) @Injection(name = MDI_GROUP + "_IS_HIDDEN", group = MDI_GROUP) private boolean hidden; public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isUnique() { return unique; } public void setUnique(boolean unique) { this.unique = unique; } public String getTimeFormat() { return timeFormat; } public void setTimeFormat(String timeFormat) { this.timeFormat = timeFormat; } public ModelAnnotation.TimeType getTimeType() { return timeType; } public void setTimeType(ModelAnnotation.TimeType timeType) { this.timeType = timeType; } public ModelAnnotation.GeoType getGeoType() { return geoType; } public void setGeoType(ModelAnnotation.GeoType geoType) { this.geoType = geoType; } public String getOrdinalField() { return ordinalField; } public void setOrdinalField(String ordinalField) { this.ordinalField = ordinalField; } public String getFormatString() { return formatString; } public void setFormatString(final String formatString) { this.formatString = formatString; } public String getParentAttribute() { return parentAttribute; } public void setParentAttribute(final String parentAttribute) { this.parentAttribute = parentAttribute; } public String getDimension() { return dimension; } public void setDimension(final String dimension) { this.dimension = dimension; } public String getHierarchy() { return hierarchy; } public void setHierarchy(final String hierarchy) { this.hierarchy = hierarchy; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getBusinessGroup() { return businessGroup; } public void setBusinessGroup(String businessGroup) { this.businessGroup = businessGroup; } @Override public String getField() { return field; } public void setField(String field) { this.field = field; } public String getLevel() { return level; } public void setLevel(String level) { this.level = level; } public String getCube() { return cube; } public void setCube(String cube) { this.cube = cube; } public boolean isHidden() { return hidden; } public void setHidden(boolean hidden) { this.hidden = hidden; } @Override public boolean apply(final ModelerWorkspace workspace, final IMetaStore metaStore) throws ModelerException { HierarchyMetaData existingHierarchy = locateHierarchy(workspace, getHierarchy()); if (existingHierarchy == null && !Const.isEmpty(getParentAttribute())) { return false; } else if (existingHierarchy != null && Const.isEmpty(getParentAttribute())) { removeHierarchy(existingHierarchy); return createNewHierarchy(workspace, resolveField(workspace)); } else if (existingHierarchy == null) { return createNewHierarchy(workspace, resolveField(workspace)); } else { return attachLevel(workspace, existingHierarchy, resolveField(workspace)); } } private String resolveField(final ModelerWorkspace workspace) throws ModelerException { String field = getField(); if (StringUtils.isBlank(field)) { if (!StringUtils.isBlank(getLevel()) && !StringUtils.isBlank(getCube())) { field = resolveFieldFromLevel(workspace, getLevel(), getCube()); setField(field); } else { throw new ModelerException( BaseMessages.getString("ModelAnnotation.resolveField.UNABLE_TO_FIND_FIELD")); } } return field; } private void removeHierarchy(final HierarchyMetaData hierarchy) { DimensionMetaData dimension = hierarchy.getDimensionMetaData(); if (dimension.contains(hierarchy)) { dimension.remove(hierarchy); } } private HierarchyMetaData locateHierarchy(final ModelerWorkspace workspace, final String name) { for (DimensionMetaData dimensionMetaData : workspace.getModel().getDimensions()) { if (dimensionMetaData.getName().equals(getDimension())) { for (HierarchyMetaData hierarchyMetaData : dimensionMetaData) { if (hierarchyMetaData.getName().equals(Const.isEmpty(name) ? getDimension() : name)) { return hierarchyMetaData; } } } } return null; } private boolean isAutoModeled(final ModelerWorkspace workspace) { try { if ((getGeoType() != null) && StringUtils.equals(workspace.getGeoContext().getDimensionName(), getDimension())) { return true; } HierarchyMetaData hierarchy = locateHierarchy(workspace, getDimension()); return hierarchy != null && hierarchy.size() == 1 && hierarchy.getLevels().get(0).getName().equals(hierarchy.getName()); } catch (Exception e) { return false; } } private boolean createNewHierarchy(final ModelerWorkspace workspace, final String column) throws ModelerException { HierarchyMetaData hierarchyMetaData = new HierarchyMetaData( Const.isEmpty(getHierarchy()) ? getDimension() : getHierarchy()); for (DimensionMetaData dimensionMetaData : workspace.getModel().getDimensions()) { if (dimensionMetaData.getName().equals(getDimension()) && !isAutoModeled(workspace)) { hierarchyMetaData.setParent(dimensionMetaData); if (dimensionMetaData.isEmpty()) { dimensionMetaData.setDimensionType(dimensionType()); } dimensionMetaData.add(hierarchyMetaData); } } if (hierarchyMetaData.getParent() == null) { DimensionMetaData dimensionMetaData = new DimensionMetaData(getDimension(), dimensionType()); dimensionMetaData.setTimeDimension(getTimeType() != null); workspace.getModel().getDimensions().add(dimensionMetaData); hierarchyMetaData.setParent(dimensionMetaData); dimensionMetaData.add(hierarchyMetaData); } LevelMetaData existingLevel = locateLevel(workspace, column); LevelMetaData ordinalAutoLevel = locateLevel(workspace, getOrdinalField()); LogicalColumn logicalColumn = locateLogicalColumn(workspace, column); if (logicalColumn == null) { return false; } LevelMetaData levelMetaData = new LevelMetaData(hierarchyMetaData, getName()); hierarchyMetaData.add(levelMetaData); fillLevelProperties(workspace, logicalColumn, levelMetaData); removeAutoLevel(workspace, existingLevel); removeAutoMeasure(workspace, column); removeAutoLevel(workspace, ordinalAutoLevel); removeAutoMeasure(workspace, getOrdinalField()); workspace.getWorkspaceHelper().populateDomain(workspace); return true; } private String dimensionType() { if (getTimeType() != null) { return OlapDimension.TYPE_TIME_DIMENSION; } return OlapDimension.TYPE_STANDARD_DIMENSION; } private void fillLevelProperties(final ModelerWorkspace workspace, final LogicalColumn logicalColumn, final LevelMetaData levelMetaData) { levelMetaData.setLogicalColumn(logicalColumn); levelMetaData.setUniqueMembers(isUnique()); levelMetaData.setHidden(isHidden()); if (getTimeType() != null) { levelMetaData.setDataRole(TimeRole.fromMondrianAttributeValue(getTimeType().name())); } if (!Const.isEmpty(getTimeFormat())) { levelMetaData.setTimeLevelFormat(getTimeFormat()); } LogicalColumn ordinalColumn = locateLogicalColumn(workspace, getOrdinalField()); if (ordinalColumn != null) { levelMetaData.setLogicalOrdinalColumn(ordinalColumn); } if (getGeoType() != null) { if (Const.isEmpty(getParentAttribute())) { removeAutoGeo(workspace); } GeoRole geoRole = workspace.getGeoContext().getGeoRoleByName(getGeoType().name()); levelMetaData.getMemberAnnotations().put("Data.Role", geoRole); } if (getDescription() != null) { levelMetaData.setDescription(getDescription()); } if (!StringUtils.isBlank(getFormatString())) { levelMetaData.getMemberAnnotations().put(InlineFormatAnnotation.INLINE_MEMBER_FORMAT_STRING, new InlineFormatAnnotation(getFormatString())); } } private void removeAutoGeo(final ModelerWorkspace workspace) { DimensionMetaDataCollection dimensions = workspace.getModel().getDimensions(); DimensionMetaData toRemove = null; GeoContext geoContext = workspace.getGeoContext(); for (DimensionMetaData dimensionMetaData : dimensions) { if (geoContext != null && dimensionMetaData.getName().equals(geoContext.getDimensionName())) { for (HierarchyMetaData hierarchyMetaData : dimensionMetaData) { if (hierarchyMetaData.getName().equals(geoContext.getDimensionName())) { for (LevelMetaData levelMetaData : hierarchyMetaData) { if (levelMetaData.getMemberAnnotations().get("Data.Role") != null) { toRemove = dimensionMetaData; break; } } } } } } if (toRemove != null) { dimensions.remove(toRemove); } } private boolean attachLevel(final ModelerWorkspace workspace, final HierarchyMetaData existingHierarchy, final String column) throws ModelerException { int parentIndex = parentIndex(existingHierarchy); if (parentIndex < 0) { return false; } else { LevelMetaData existingLevel = locateLevel(workspace, column); LevelMetaData ordinalAutoLevel = locateLevel(workspace, getOrdinalField()); LogicalColumn logicalColumn = locateLogicalColumn(workspace, column); if (logicalColumn == null) { return false; } LevelMetaData levelMetaData = new LevelMetaData(existingHierarchy, getName()); existingHierarchy.add(parentIndex + 1, levelMetaData); fillLevelProperties(workspace, logicalColumn, levelMetaData); removeAutoLevel(workspace, existingLevel); removeAutoMeasure(workspace, column); removeAutoLevel(workspace, ordinalAutoLevel); removeAutoMeasure(workspace, getOrdinalField()); workspace.getWorkspaceHelper().populateDomain(workspace); return true; } } private int parentIndex(final HierarchyMetaData existingHierarchy) { List<LevelMetaData> levels = existingHierarchy.getLevels(); for (int i = 0; i < levels.size(); i++) { LevelMetaData levelMetaData = levels.get(i); if (levelMetaData.getName().equals(getParentAttribute())) { return i; } } return -1; } @Override public void populate(final Map<String, Serializable> propertiesMap) { super.populate(propertiesMap); // let base class handle primitives, etc. try { // correctly convert time type if (propertiesMap.containsKey(TIME_TYPE_ID)) { Serializable value = propertiesMap.get(TIME_TYPE_ID); if (value != null) { setTimeType(ModelAnnotation.TimeType.valueOf(value.toString())); } } // correctly convert geo type if (propertiesMap.containsKey(GEO_TYPE_ID)) { Serializable value = propertiesMap.get(GEO_TYPE_ID); if (value != null) { setGeoType(ModelAnnotation.GeoType.valueOf(value.toString())); } } } catch (Exception e) { // ignore logger.warning(e.getLocalizedMessage()); } } @Override public ModelAnnotation.Type getType() { return ModelAnnotation.Type.CREATE_ATTRIBUTE; } @Override public String getSummary() { return BaseMessages.getString(MSG_CLASS, summaryMsgKey(), getName(), Const.isEmpty(getHierarchy()) ? "" : " " + getHierarchy(), getParentAttribute()); } private String summaryMsgKey() { if (Const.isEmpty(getParentAttribute())) { return "Modeler.CreateAttribute.Summary.noparent"; } return "Modeler.CreateAttribute.Summary"; } @Override public boolean apply(Document schema) throws ModelerException { throw new UnsupportedOperationException(); } @Override public void validate() throws ModelerException { if (StringUtils.isBlank(getName())) { throw new ModelerException(BaseMessages.getString(MSG_CLASS, "ModelAnnotation.CreateAttribute.validation.ATTRIBUTE_NAME_REQUIRED")); } if (StringUtils.isBlank(getField()) && (StringUtils.isBlank(getLevel()) || StringUtils.isBlank(getCube()))) { throw new ModelerException(BaseMessages.getString(MSG_CLASS, "ModelAnnotation.CreateAttribute.validation.FIELD_OR_LEVEL_NOT_PROVIDED")); } if (StringUtils.isBlank(getDimension())) { throw new ModelerException(BaseMessages.getString(MSG_CLASS, "ModelAnnotation.CreateAttribute.validation.PARENT_PROVIDED_MISSING_DIMENSION")); } if (StringUtils.isNotBlank(getParentAttribute()) && StringUtils.isBlank(getDimension())) { throw new ModelerException(BaseMessages.getString(MSG_CLASS, "ModelAnnotation.CreateAttribute.validation.PARENT_PROVIDED_MISSING_DIMENSION")); } } /** * CreateAttribute objects are considered logically equal if the name, dimension, and hierarchy are equal to * the equivalent fields in the object under comparison * @param obj * @return */ @Override public boolean equalsLogically(AnnotationType obj) { if (obj == null || obj.getClass() != getClass()) { return false; } CreateAttribute that = (CreateAttribute) obj; EqualsBuilder eq = new EqualsBuilder(); // by default just see if the name is the same String thatName = that.getName() == null ? null : that.getName().toLowerCase(); String myName = getName() == null ? null : getName().toLowerCase(); eq.append(myName, thatName); String thatDimension = that.getDimension() == null ? null : that.getDimension().toLowerCase(); String myDimension = getDimension() == null ? null : getDimension().toLowerCase(); eq.append(myDimension, thatDimension); String thatHierarchy = that.getHierarchy() == null ? null : that.getHierarchy().toLowerCase(); String myHierarchy = getHierarchy() == null ? null : getHierarchy().toLowerCase(); // fall back to default hierarchy name equal to dimension name if (thatHierarchy == null) { thatHierarchy = thatDimension; } if (myHierarchy == null) { myHierarchy = myDimension; } eq.append(myHierarchy, thatHierarchy); return eq.isEquals(); } }