Java tutorial
/** * Copyright (c) 2014 Baidu, Inc. All Rights Reserved. * * 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 com.baidu.rigel.biplatform.ma.model.builder.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import com.baidu.rigel.biplatform.ac.minicube.CallbackLevel; import com.baidu.rigel.biplatform.ac.minicube.CallbackMeasure; import com.baidu.rigel.biplatform.ac.minicube.ExtendMinicubeMeasure; import com.baidu.rigel.biplatform.ac.minicube.MiniCube; import com.baidu.rigel.biplatform.ac.minicube.MiniCubeDimension; import com.baidu.rigel.biplatform.ac.minicube.MiniCubeLevel; import com.baidu.rigel.biplatform.ac.minicube.MiniCubeSchema; import com.baidu.rigel.biplatform.ac.model.Cube; import com.baidu.rigel.biplatform.ac.model.Dimension; import com.baidu.rigel.biplatform.ac.model.DimensionType; import com.baidu.rigel.biplatform.ac.model.Level; import com.baidu.rigel.biplatform.ac.model.Measure; import com.baidu.rigel.biplatform.ac.model.MeasureType; import com.baidu.rigel.biplatform.ac.model.Schema; import com.baidu.rigel.biplatform.ac.util.DeepcopyUtils; import com.baidu.rigel.biplatform.ma.comm.util.ParamValidateUtils; import com.baidu.rigel.biplatform.ma.model.builder.Director; import com.baidu.rigel.biplatform.ma.model.meta.DimTableMetaDefine; import com.baidu.rigel.biplatform.ma.model.meta.StarModel; import com.google.common.collect.Lists; import com.google.common.collect.Maps; /** * * Only implementation of the <tt>Director</tt> interface.Implements * all optional {@link Director} operations. * <p> * <hr/> * all know subclasses<br/>: * None * </p> * * * @see com.baidu.rigel.biplatform.ac.model.Schema * @see com.baidu.rigel.biplatform.ma.model.meta.StarModel * @since JDK1.8 or after * @version Silkroad 1.0.1 * @author david.wang * */ @Service public class DirectorImpl implements Director { /** * the builder service of schema */ private SchemaBuilder schemaBuilder = new SchemaBuilder(); /** * logger */ private Logger logger = LoggerFactory.getLogger(DirectorImpl.class); /** * {@inheritDoc} */ @Override public Schema getSchema(StarModel[] starModels) { // check the input validate or not if (!ParamValidateUtils.check("starModels", starModels)) { return null; } logger.info("begin generate schema with start models"); // build schema with the star model reference datasource's id MiniCubeSchema schema = (MiniCubeSchema) buildSchema(starModels[0].getDsId()); if (!ParamValidateUtils.check("schema", schema)) { return null; } // build cubes for the schema schema.setCubes(buildCubes(schema, starModels)); return schema; } /** * {@inheritDoc} */ @Override public Schema modifySchemaWithNewModel(Schema schema, StarModel[] starModels) { // check input if invalidate return if (!ParamValidateUtils.check("starModels", starModels)) { return schema; } // make sure schema correct if (!ParamValidateUtils.check("schema", schema)) { throw new IllegalStateException("ori schema can not be null or must include cubes"); } if (!ParamValidateUtils.check("cubes", schema.getCubes())) { throw new IllegalStateException("ori schema can not be null or must include cubes"); } // create new map store the new cubes which generate from new star models Map<String, MiniCube> newCubes = Maps.newLinkedHashMap(); // because the new star models lost some info, so create new schema and copy lost info to the schema MiniCubeSchema newSchema = new MiniCubeSchema(); // copy lost info newSchema.setDatasource(schema.getDatasource()); newSchema.setId(schema.getId()); newSchema.setName(schema.getName()); newSchema.setVisible(true); newSchema.setDescription(schema.getDescription()); CubeBuilder builder = new CubeBuilder(); for (StarModel model : starModels) { Cube cube = schema.getCubes().get(model.getCubeId()); // maybe this is new cube if (cube == null) { cube = builder.buildCube(model); } else { cube = modifyCubeWithModel(builder, schema, model); } if (cube != null) { ((MiniCube) cube).setSchema(newSchema); newCubes.put(cube.getId(), (MiniCube) cube); } } ((MiniCubeSchema) newSchema).setCubes(newCubes); return newSchema; } /** * * update {@link Cube} with {@link StarModel} * * @param builder -- CubeBuilder * @param oriSchema -- original schema * @param starModel -- new star model * @return cube -- already update cube * @see com.baidu.rigel.biplatform.ma.model.builder.impl.CubeBuilder * @see com.baidu.rigel.biplatform.ac.model.Schema * */ private Cube modifyCubeWithModel(CubeBuilder builder, Schema oriSchema, StarModel starModel) { MiniCube oriCube = (MiniCube) oriSchema.getCubes().get(starModel.getCubeId()); // StarModelBuilder modelBuilder = new StarModelBuilder(); // StarModel oriModel = modelBuilder.buildModel((MiniCube) oriCube); // if true the star model not changed // if (oriModel.equals(starModel)) { // return oriCube; // } MiniCube cube = new MiniCube(); cube.setCaption(oriCube.getCaption()); cube.setId(oriCube.getId()); cube.setMutilple(oriCube.isMutilple()); cube.setSource(oriCube.getSource()); cube.setName(oriCube.getName()); cube.setVisible(oriCube.isVisible()); Map<String, Measure> newMeasures = modifyMeasures(starModel, oriCube); cube.setMeasures(newMeasures); // store the newest dimension Map<String, Dimension> dims = new HashMap<String, Dimension>(); Map<String, Dimension> oriDims = oriCube.getDimensions(); DimensionBuilder dimBuilder = new DimensionBuilder(); List<Dimension> newDimensions = Lists.newArrayList(); for (DimTableMetaDefine dimTable : starModel.getDimTables()) { Dimension[] buildDims = dimBuilder.buildDimensions(dimTable, starModel.getFactTable()); Collections.addAll(newDimensions, buildDims); } dims = addOrReplaceDims(oriDims, newDimensions); dims = modifyDimGroup(dims, DeepcopyUtils.deepCopy(oriDims)); resetMeasures(dims, oriCube.getDimensions(), cube); cube.setDimensions(dims); return cube; } /** * ???? * @param dims * @param oriDims * @param cube */ private void resetMeasures(Map<String, Dimension> dims, Map<String, Dimension> oriDims, MiniCube cube) { Iterator<String> it = cube.getMeasures().keySet().iterator(); final Map<String, Dimension> tmp = Maps.newHashMap(); oriDims.values().forEach(dim -> { if (dim.getType() != DimensionType.GROUP_DIMENSION && dim.getTableName().equals(cube.getSource())) { tmp.put(dim.getName(), dim); } }); while (it.hasNext()) { Measure m = cube.getMeasures().get(it.next()); if (tmp.containsKey(m.getName())) { Dimension dim = tmp.get(m.getName()); dims.put(dim.getId(), dim); it.remove(); } } } /** * * modify {@link Dimension} group define * @param dims -- the newest dimensions which update through star model * @param oriDims -- original dimensions * @return the newest dimensions map, key is dimension's id * */ private Map<String, Dimension> modifyDimGroup(Map<String, Dimension> dims, Map<String, Dimension> oriDims) { Set<String> allLevelIds = getAllLevels(dims); Iterator<Entry<String, Dimension>> it = oriDims.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, Dimension> tmpDim = it.next(); it.remove(); Dimension dim = tmpDim.getValue(); if (dim.getType() == DimensionType.GROUP_DIMENSION) { Iterator<Map.Entry<String, Level>> levelIterator = dim.getLevels().entrySet().iterator(); for (; levelIterator.hasNext();) { Map.Entry<String, Level> tmp = levelIterator.next(); Level value = tmp.getValue(); String key = value.getFactTableColumn() + "_" + value.getDimTable() + "_" + value.getName(); if (!allLevelIds.contains(key)) { levelIterator.remove(); } } if (dim.getLevels().size() > 0) { dims.put(dim.getId(), dim); } } } return dims; } /** * * get all dimensions's levels id list * @param dims -- dimension instance map * @return the set which contains id of dimension's levels * @see com.baidu.rigel.biplatform.ac.model.Dimension * */ private Set<String> getAllLevels(Map<String, Dimension> dims) { Set<String> levelKeys = new HashSet<String>(); for (Map.Entry<String, Dimension> dim : dims.entrySet()) { String key = dim.getValue().getFacttableColumn() + "_" + dim.getValue().getTableName(); dim.getValue().getLevels().values().forEach(level -> { levelKeys.add(key + "_" + level.getName()); }); } return levelKeys; } /** * * add or update {@link Dimension} define through the dimensions which generate from new star model * * @param oriDims -- original dimension which already defined in schema * @param buildDims -- new dimension which generate from new star model * @return the newest dimension map, key is dimension's id * */ private Map<String, Dimension> addOrReplaceDims(Map<String, Dimension> oriDims, List<Dimension> buildDims) { Map<String, Dimension> dims = new LinkedHashMap<String, Dimension>(); // if (oriDims.isEmpty()) { // buildDims.forEach(dim -> { // oriDims.put(dim.getId(), dim); // }); // return dims; // } final Map<String, Dimension> dimIdents = new LinkedHashMap<String, Dimension>(); oriDims.values().forEach(dim -> { dimIdents.put(buildDimIdent(dim), dim); }); buildDims.forEach(dim -> { String dimIdent = buildDimIdent(dim); if (dimIdents.containsKey(dimIdent)) { Dimension tmp = dimIdents.get(dimIdent); ((MiniCubeDimension) dim).setId(tmp.getId()); dims.put(tmp.getId(), dim); } else { dims.put(dim.getId(), dim); } }); return dims; } /** * * generate dimensions's identification through {@link Dimension} instance * @param dim -- dimension instance * @return dimIdent -- dimension identification * */ private String buildDimIdent(Dimension dim) { String ident = dim.getTableName(); Level level = dim.getLevels().values().toArray(new Level[0])[0]; if (level instanceof MiniCubeLevel) { return ident + "_" + ((MiniCubeLevel) level).getSource(); } return ident + "_" + ((CallbackLevel) level).getName(); } /** * * update the cube's measures: * remove unused measures, add new measures, copy the calculate measures and no changed measures * * @param starModel -- star model * @param oriCube -- original cube * @return the new measures, key is measure id * @see com.baidu.rigel.biplatform.ac.model.Measure * */ private Map<String, Measure> modifyMeasures(StarModel starModel, MiniCube oriCube) { Map<String, Measure> newMeasures = new HashMap<String, Measure>(); Map<String, Measure> oriMeasures = oriCube.getMeasures(); // store new reference column info final Set<String> refCol = new HashSet<String>(); // iterate all the dimension table and find the reference column starModel.getDimTables().forEach(dimTable -> { refCol.add(dimTable.getReference().getMajorColumn()); }); final Map<String, String> oriMeasureNameRep = new HashMap<String, String>(); // remove all the measures which already convert to dimension oriMeasures.values().stream().filter(oriMeasure -> { return !refCol.contains(oriMeasure.getDefine()) && !StringUtils.isEmpty(oriMeasure.getName()); }).map(oriMeasure -> { return oriMeasure.getName() + "&&" + oriMeasure.getId(); }).distinct().forEach(str -> { String[] tmp = str.split("&&"); oriMeasureNameRep.put(tmp[0], tmp[1]); }); final MeasureBuilder measureBuilder = new MeasureBuilder(); starModel.getFactTable().getColumnList().stream().forEach(col -> { // if true measure already convert to dimension if (!refCol.contains(col.getName()) && !StringUtils.isEmpty(col.getName())) { // old measure if (oriMeasureNameRep.containsKey(col.getName())) { String id = oriMeasureNameRep.get(col.getName()); newMeasures.put(id, oriMeasures.get(id)); } else { Measure m = measureBuilder.buildMeasure(col); newMeasures.put(m.getId(), m); } } }); // ??? oriCube.getMeasures().forEach((k, v) -> { if (v instanceof CallbackMeasure) { newMeasures.put(v.getId(), v); } if (v.getType() == MeasureType.CAL || v.getType() == MeasureType.SR || v.getType() == MeasureType.RR) { ExtendMinicubeMeasure m = (ExtendMinicubeMeasure) v; if (checkRefMeasuer(m.getRefIndNames(), newMeasures)) { newMeasures.put(v.getId(), v); } } }); return newMeasures; } /** * * @param newMeasures * @param refNames * @return */ private boolean checkRefMeasuer(Set<String> refNames, Map<String, Measure> newMeasures) { boolean rs = true; if (refNames == null || refNames.size() == 0) { return rs; } String[] measuerNames = newMeasures.values().stream().map(m -> { return m.getName(); }).toArray(String[]::new); List<String> tmp = Lists.newArrayList(); Collections.addAll(tmp, measuerNames); for (String ref : refNames) { if (!tmp.contains(ref)) { return false; } } // for (String str : refNames) { // if (refNames.contains(entry.getKey()) || refNames.contains(entry.getValue().getName())) { // continue; // } // } return rs; } /** * * {@inheritDoc} * */ @Override public StarModel[] getStarModel(Schema schema) { if (schema == null) { logger.error("can not create star model with null schema"); return new StarModel[0]; } Collection<? extends Cube> cubes = schema.getCubes().values(); if (cubes == null || cubes.size() == 0) { logger.error("can not create star model with null cubes"); return new StarModel[0]; } List<StarModel> rs = new ArrayList<StarModel>(); StarModelBuilder modelBuilder = new StarModelBuilder(); for (Cube cube : cubes) { StarModel model = modelBuilder.buildModel((MiniCube) cube); if (model == null) { continue; } rs.add(model); } logger.info("create star model with schema successfully"); return rs.toArray(new StarModel[0]); } /** * build cubes({@link Cube}'s Map) with star model({@link StarModel}) * * @param starModels -- star model array * @param schema -- schema instance * @return if success return map instance, include all cubes but empty, key is cube's id * */ private Map<String, MiniCube> buildCubes(Schema schema, StarModel[] starModels) { Map<String, MiniCube> cubes = new HashMap<String, MiniCube>(); logger.info("begin create starModel"); // make sure star model is not empty if (starModels == null) { logger.info("star models is null"); return cubes; } if (starModels.length <= 0) { logger.info("star models's size is 0"); return cubes; } // create cube CubeBuilder builder = new CubeBuilder(); for (StarModel model : starModels) { Cube cube = builder.buildCube(model); if (cube != null) { ((MiniCube) cube).setSchema(schema); cubes.put(cube.getId(), (MiniCube) cube); } } logger.info("create cube successfully"); return cubes; } /** * * build {@link Schema} with datasource's id * * @param dsId -- datasource's id * @return schema -- schema instance * */ private Schema buildSchema(String dsId) { if (StringUtils.isEmpty(dsId)) { logger.error("datasource id can not be null"); throw new IllegalStateException("star model's datasource id is null"); } Schema schema = schemaBuilder.buildSchema(dsId); if (schema == null) { logger.error("can not be create schema with starModel"); return null; } logger.info("transform model to schema successfully " + schema); return schema; } }