@SuppressWarnings("deprecation")
  public void addColumn(final LogicalColumn column, final Element tableNode, final String locale) {
    Element columnNode = tableNode.addElement("column"); // $NON-NLS-1$

    if (column.getId() != null) {
      columnNode.addElement("column_id").setText(column.getId()); // $NON-NLS-1$
    }
    if (column.getName(locale) != null) {
      columnNode.addElement("column_name").setText(column.getName(locale)); // $NON-NLS-1$
    }
    if (column.getDescription(locale) != null) {
      columnNode
          .addElement("column_description")
          .setText(column.getDescription(locale)); // $NON-NLS-1$
    }
    if (column.getFieldType() != null) {
      // TODO this should take a locale
      String desc = column.getFieldType().getDescription();
      desc = org.pentaho.pms.messages.Messages.getString(desc);
      columnNode.addElement("column_field_type").setText(desc); // $NON-NLS-1$
    }
    DataType dataType = column.getDataType();
    if (dataType != null) {
      columnNode.addElement("column_type").setText(dataType.getName()); // $NON-NLS-1$
    }
    if (column.getProperty("lookup") != null) { // $NON-NLS-1$
      columnNode.addElement("column_lookup").setText("true"); // $NON-NLS-1$ //$NON-NLS-2$
    }
  }
  @Test
  public void testPopulateCategories_MultipleCategoriesAggregationTypesAndFormatMasks()
      throws ModelerException {
    ModelerWorkspaceHelper helper = new ModelerWorkspaceHelper(LOCALE);
    LogicalModel logicalModel = workspace.getLogicalModel(ModelerPerspective.REPORTING);
    helper.autoModelFlat(workspace);
    helper.autoModelRelationalFlat(workspace);
    spiceUpRelationalModel(workspace.getRelationalModel());
    helper.populateCategories(workspace);

    List<AvailableTable> tablesList = workspace.getAvailableTables().getAsAvailableTablesList();

    List<Category> categories = logicalModel.getCategories();
    assertEquals(2, categories.size());
    assertEquals(
        tablesList.get(0).getAvailableFields().size(),
        categories.get(0).getLogicalColumns().size());
    System.out.println(logicalModel.getLogicalTables().get(0).getLogicalColumns().size());

    assertEquals(1, categories.get(1).getLogicalColumns().size());

    for (int i = 0; i < categories.size(); i++) {

      for (LogicalColumn lCol : categories.get(i).getLogicalColumns()) {
        FieldMetaData orig = null;
        for (FieldMetaData fieldMetaData : workspace.getRelationalModel().getCategories().get(i)) {
          if (lCol.getId().equals(fieldMetaData.getLogicalColumn().getId())) {
            orig = fieldMetaData;
            break;
          }
        }
        assertNotNull(orig);
        assertEquals(orig.getDefaultAggregation(), lCol.getAggregationType());
        if (orig.getFormat().equals("NONE")) {
          if (orig.getLogicalColumn().getDataType() == DataType.NUMERIC) {
            assertTrue(((String) lCol.getProperty("mask")).indexOf("#") > -1);
          } else {
            assertTrue(lCol.getProperty("mask") == null);
          }
        } else {
          assertEquals(orig.getFormat(), lCol.getProperty("mask"));
        }
      }
    }
  }
  @Test
  public void testPopulateCategories() throws ModelerException {
    ModelerWorkspaceHelper helper = new ModelerWorkspaceHelper(LOCALE);
    LogicalModel logicalModel = workspace.getDomain().getLogicalModels().get(0);
    List<AvailableTable> tablesList = workspace.getAvailableTables().getAsAvailableTablesList();

    int fields = tablesList.get(0).getAvailableFields().size();
    helper.autoModelFlat(workspace);
    helper.autoModelRelationalFlat(workspace);
    helper.populateCategories(workspace);

    List<Category> categories = logicalModel.getCategories();

    assertEquals(1, categories.size());
    assertEquals(fields, tablesList.get(0).getAvailableFields().size());
    System.out.println(logicalModel.getLogicalTables().get(0).getLogicalColumns().size());
    assertEquals(
        tablesList.get(0).getAvailableFields().size(),
        categories.get(0).getLogicalColumns().size());

    for (LogicalColumn lCol : categories.get(0).getLogicalColumns()) {
      FieldMetaData orig = null;
      for (FieldMetaData fieldMetaData : workspace.getRelationalModel().getCategories().get(0)) {
        if (lCol.getId().equals(fieldMetaData.getLogicalColumn().getId())) {
          orig = fieldMetaData;
          break;
        }
      }
      assertNotNull(orig);
      assertEquals(orig.getDefaultAggregation(), lCol.getAggregationType());
      if (orig.getFormat().equals("NONE")) {
        if (orig.getLogicalColumn().getDataType() == DataType.NUMERIC) {
          assertTrue("#".equals(lCol.getProperty("mask")));
        } else {
          assertTrue(lCol.getProperty("mask") == null);
        }
      } else {
        assertEquals(orig.getFormat(), lCol.getProperty("mask"));
      }
    }
  }
Esempio n. 4
0
  // this method signature is intended to provide a simpler path for unit testing the upConvert
  // method on its own
  protected void setDomain(Domain d, boolean upConvertDesired) {
    this.domain = d;
    this.setModelIsChanging(true);
    this.setRelationalModelIsChanging(true);
    this.model.getDimensions().clear();
    this.model.getMeasures().clear();
    this.relationalModel.getCategories().clear();
    this.availableTables.clear();

    if (upConvertDesired) {
      upConvertLegacyModel();
    }
    List<IAvailableItem> items = new ArrayList<IAvailableItem>();
    for (IPhysicalTable table : domain.getPhysicalModels().get(0).getPhysicalTables()) {
      Boolean isFact = (Boolean) table.getProperty("FACT_TABLE");
      items.add(new AvailableTable(table, isFact == null ? false : isFact.booleanValue()));
    }

    availableTables.setChildren(items);

    fireTablesChanged();

    LogicalModel lModel = domain.getLogicalModels().get(0);

    setModelName(lModel.getName(workspaceHelper.getLocale()));
    setRelationalModelName(lModel.getName(workspaceHelper.getLocale()));

    // Set the type of modeling session. This will propagate to the UI
    if (supportsOlap(domain)) {
      this.setModellingMode(ModelerMode.ANALYSIS_AND_REPORTING);
    } else {
      this.setModellingMode(ModelerMode.REPORTING_ONLY);
    }

    lModel = getLogicalModel(ModelerPerspective.ANALYSIS);
    List<OlapDimension> theDimensions = null;
    if (lModel != null) {
      theDimensions = (List) lModel.getProperty(LogicalModel.PROPERTY_OLAP_DIMS); // $NON-NLS-1$
    }
    if (theDimensions != null) {
      Iterator<OlapDimension> theDimensionItr = theDimensions.iterator();
      while (theDimensionItr.hasNext()) {
        OlapDimension theDimension = theDimensionItr.next();

        DimensionMetaData theDimensionMD =
            new DimensionMetaData(theDimension.getName(), theDimension.getType());
        theDimensionMD.setTimeDimension(theDimension.isTimeDimension());
        List<OlapHierarchy> theHierarchies = (List) theDimension.getHierarchies();
        Iterator<OlapHierarchy> theHierarchiesItr = theHierarchies.iterator();
        while (theHierarchiesItr.hasNext()) {
          OlapHierarchy theHierarchy = theHierarchiesItr.next();
          HierarchyMetaData theHierarchyMD = new HierarchyMetaData(theHierarchy.getName());

          List<OlapHierarchyLevel> theLevels = theHierarchy.getHierarchyLevels();
          Iterator<OlapHierarchyLevel> theLevelsItr = theLevels.iterator();
          while (theLevelsItr.hasNext()) {
            OlapHierarchyLevel theLevel = theLevelsItr.next();
            LevelMetaData theLevelMD = new LevelMetaData(theHierarchyMD, theLevel.getName());

            theLevelMD.setParent(theHierarchyMD);

            theLevelMD.setUniqueMembers(theLevel.isHavingUniqueMembers());
            if (theDimensionMD.isTimeDimension()) {
              TimeRole role = TimeRole.fromMondrianAttributeValue(theLevel.getLevelType());
              if (role != null) {
                theLevelMD.setDataRole(role);
              }
            }

            // Make sure we're dealing with the OLAP copy. Note that duplicated columns will have an
            // OLAP_[0-9]+ at the
            // end
            String refID;
            LogicalColumn olapCol;

            olapCol = theLevel.getReferenceColumn();
            if (olapCol != null) {
              refID = olapCol.getId();
              if (!refID.endsWith(BaseModelerWorkspaceHelper.OLAP_SUFFIX)
                  && !refID.contains(BaseModelerWorkspaceHelper.OLAP_SUFFIX + "_")) {
                olapCol = ModelerConversionUtil.findCorrespondingOlapColumn(olapCol, lModel);
                theLevel.setReferenceColumn(olapCol);
              }
              theLevelMD.setLogicalColumn(olapCol);
            }

            olapCol = theLevel.getReferenceOrdinalColumn();
            if (olapCol != null) {
              refID = olapCol.getId();
              if (!refID.endsWith(BaseModelerWorkspaceHelper.OLAP_SUFFIX)
                  && !refID.contains(BaseModelerWorkspaceHelper.OLAP_SUFFIX + "_")) {
                olapCol = ModelerConversionUtil.findCorrespondingOlapColumn(olapCol, lModel);
                theLevel.setReferenceOrdinalColumn(olapCol);
              }
              theLevelMD.setLogicalOrdinalColumn(olapCol);
            }

            olapCol = theLevel.getReferenceCaptionColumn();
            if (olapCol != null) {
              refID = olapCol.getId();
              if (!refID.endsWith(BaseModelerWorkspaceHelper.OLAP_SUFFIX)
                  && !refID.contains(BaseModelerWorkspaceHelper.OLAP_SUFFIX + "_")) {
                olapCol = ModelerConversionUtil.findCorrespondingOlapColumn(olapCol, lModel);
                theLevel.setReferenceCaptionColumn(olapCol);
              }
              theLevelMD.setLogicalCaptionColumn(olapCol);
            }
            // get any logicalColumns and turn them into member properties
            if (theLevel.getLogicalColumns() != null && theLevel.getLogicalColumns().size() > 0) {
              for (LogicalColumn lc : theLevel.getLogicalColumns()) {
                // BISERVER-11578 - Protect against null lc's in the collection. We still need to
                // investigate why this can happen in the model.
                if (lc == null) {
                  continue;
                }

                if (!lc.getId().endsWith(BaseModelerWorkspaceHelper.OLAP_SUFFIX)
                    && !lc.getId().contains(BaseModelerWorkspaceHelper.OLAP_SUFFIX + "_")) {
                  // not pointing to the olap col
                  lc = ModelerConversionUtil.findCorrespondingOlapColumn(lc, lModel);
                }
                MemberPropertyMetaData memberProp =
                    new MemberPropertyMetaData(theLevelMD, lc.getName(workspaceHelper.getLocale()));
                memberProp.setLogicalColumn(lc);
                memberProp.setDescription(lc.getDescription(workspaceHelper.getLocale()));
                theLevelMD.add(memberProp);
              }
            }
            List<OlapAnnotation> annotations = theLevel.getAnnotations();
            if (annotations != null) {
              for (OlapAnnotation anno : annotations) {
                IMemberAnnotation annoMeta = MemberAnnotationFactory.create(anno);
                theLevelMD.getMemberAnnotations().put(anno.getName(), annoMeta);
              }
            }
            theHierarchyMD.add(theLevelMD);
          }

          theHierarchyMD.setParent(theDimensionMD);
          theDimensionMD.add(theHierarchyMD);
        }
        this.model.getDimensions().add(theDimensionMD);
      }
    }
    List<OlapCube> theCubes = null;
    if (lModel != null) {
      theCubes = (List) lModel.getProperty(LogicalModel.PROPERTY_OLAP_CUBES); // $NON-NLS-1$
    }
    if (theCubes != null) {
      Iterator<OlapCube> theCubeItr = theCubes.iterator();
      while (theCubeItr.hasNext()) {
        OlapCube theCube = theCubeItr.next();

        List<OlapMeasure> theMeasures = theCube.getOlapMeasures();
        Iterator<OlapMeasure> theMeasuresItr = theMeasures.iterator();
        while (theMeasuresItr.hasNext()) {
          OlapMeasure theMeasure = theMeasuresItr.next();

          MeasureMetaData theMeasureMD = new MeasureMetaData(workspaceHelper.getLocale());

          if (theMeasure.getName() == null || theMeasure.getName().length() == 0) {
            theMeasureMD.setName(
                theMeasure.getLogicalColumn().getName(workspaceHelper.getLocale()));
          } else {
            theMeasureMD.setName(theMeasure.getName());
          }
          theMeasureMD.setFormat(
              (String) theMeasure.getLogicalColumn().getProperty("mask")); // $NON-NLS-1$
          theMeasureMD.setDefaultAggregation(theMeasure.getLogicalColumn().getAggregationType());
          String possibleMeasureName = theMeasure.getLogicalColumn().getId();
          if (!theMeasure
                  .getLogicalColumn()
                  .getId()
                  .endsWith(BaseModelerWorkspaceHelper.OLAP_SUFFIX)
              && !theMeasure
                  .getLogicalColumn()
                  .getId()
                  .contains(BaseModelerWorkspaceHelper.OLAP_SUFFIX + "_")) {
            // change the backing column to the olap version
            LogicalColumn olapCol =
                ModelerConversionUtil.findCorrespondingOlapColumn(
                    theMeasure.getLogicalColumn(), lModel);
            theMeasure.setLogicalColumn(olapCol);
          }

          // BISERVER-6077 - Mondrian exporter uses logical column names as measure names, make sure
          // they get set
          // properly
          LogicalColumn lCol = theMeasure.getLogicalColumn();
          Set<String> locales = lCol.getName().getLocales();
          String[] stringLocals = locales.toArray(new String[] {});
          if (stringLocals != null && stringLocals.length > 0) {
            if (theMeasure.getName() == null || theMeasure.getName().trim().length() == 0) {
              theMeasure.setName(possibleMeasureName);
            }
            lCol.setName(new LocalizedString(stringLocals[0], theMeasure.getName()));
          }

          theMeasureMD.setLogicalColumn(lCol);
          this.model.getMeasures().add(theMeasureMD);
        }
      }
    }

    lModel = this.getLogicalModel(ModelerPerspective.REPORTING);
    int i = 1;
    for (Category cat : lModel.getCategories()) {
      String catName =
          BaseModelerWorkspaceHelper.getCleanCategoryName(
              cat.getName(workspaceHelper.getLocale()), this, i++);
      CategoryMetaData catMeta = new CategoryMetaData(catName);
      for (LogicalColumn col : cat.getLogicalColumns()) {
        LogicalTable table = col.getLogicalTable();

        if (!table.getLogicalColumns().contains(col)) {
          table.addLogicalColumn(col);
        }

        Object formatMask = col.getProperty("mask");
        String colName = col.getName(workspaceHelper.getLocale());
        AggregationType aggType = col.getAggregationType();

        FieldMetaData field =
            new FieldMetaData(
                catMeta,
                colName,
                formatMask == null ? null : formatMask.toString(),
                colName,
                workspaceHelper.getLocale());
        if (aggType != null) {
          field.setDefaultAggregation(aggType);
        } else {
          field.setDefaultAggregation(AggregationType.NONE);
        }
        field.setLogicalColumn(col);

        catMeta.add(field);
      }
      this.getRelationalModel().getCategories().add(catMeta);
    }

    this.setModelIsChanging(false, true);
    this.setRelationalModelIsChanging(false, true);
  }
  public Document getLookup() {
    // Create a document that describes the result
    Document doc = DocumentHelper.createDocument();

    Element root = doc.addElement("metadata"); // $NON-NLS-1$

    if (domainName == null) {
      // we can't do this without a model
      root.addElement("message")
          .setText(
              Messages.getInstance()
                  .getString(
                      "PMDUIComponent.USER_NO_DOMAIN_SPECIFIED")); //$NON-NLS-1$ //$NON-NLS-2$
      return doc;
    }

    if (modelId == null) {
      // we can't do this without a view
      root.addElement("message")
          .setText(
              Messages.getInstance()
                  .getString(
                      "PMDUIComponent.USER_NO_MODEL_SPECIFIED")); //$NON-NLS-1$ //$NON-NLS-2$
      return doc;
    }

    if (columnId == null) {
      // we can't do this without a view
      root.addElement("message")
          .setText(
              Messages.getInstance()
                  .getString(
                      "PMDUIComponent.USER_NO_COLUMN_SPECIFIED")); //$NON-NLS-1$ //$NON-NLS-2$
      return doc;
    }

    Domain domain = getMetadataRepository().getDomain(domainName);
    String locale =
        LocaleHelper.getClosestLocale(LocaleHelper.getLocale().toString(), domain.getLocaleCodes());
    LogicalModel model =
        domain.findLogicalModel(modelId); // This is the business view that was selected.
    if (model == null) {
      root.addElement("message")
          .setText(
              Messages.getInstance()
                  .getString(
                      "PMDUIComponent.USER_MODEL_LOADING_ERROR",
                      modelId)); //$NON-NLS-1$ //$NON-NLS-2$
      return doc;
    }

    LogicalColumn column = model.findLogicalColumn(columnId);
    if (column == null) {
      root.addElement("message")
          .setText(
              Messages.getInstance()
                  .getString("PMDUIComponent.USER_COLUMN_NOT_FOUND")); // $NON-NLS-1$ //$NON-NLS-2$
      return doc;
    }

    // Temporary hack to get the BusinessCategory. When fixed properly, you should be able to
    // interrogate the
    // business column thingie for it's containing BusinessCategory.
    Category view = null;
    for (Category category : model.getCategories()) {
      for (LogicalColumn col : category.getLogicalColumns()) {
        if (col.getId().equals(column.getId())) {
          view = category;
          break;
        }
      }
    }

    if (view == null) {
      root.addElement("message")
          .setText(
              Messages.getInstance()
                  .getString("PMDUIComponent.USER_VIEW_NOT_FOUND")); // $NON-NLS-1$ //$NON-NLS-2$
      return doc;
    }

    String mql =
        "<mql><domain_type>relational</domain_type><domain_id>"
            + domainName
            + "</domain_id><model_id>"
            + modelId
            + "</model_id>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    if (column.getProperty("lookup") == null) { // $NON-NLS-1$
      mql +=
          "<selection><view>"
              + view.getId()
              + "</view><column>"
              + column.getId()
              + "</column></selection>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
      mql +=
          "<orders><order><direction>asc</direction><view_id>"
              + view.getId()
              + "</view_id><column_id>"
              + column.getId()
              + "</column_id></order></orders>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

    } else {

      String lookup = (String) column.getProperty("lookup"); // $NON-NLS-1$
      // assume model and view are the same...
      StringTokenizer tokenizer1 = new StringTokenizer(lookup, ";"); // $NON-NLS-1$
      while (tokenizer1.hasMoreTokens()) {
        StringTokenizer tokenizer2 =
            new StringTokenizer(tokenizer1.nextToken(), "."); // $NON-NLS-1$
        if (tokenizer2.countTokens() == 2) {
          String lookupViewId = tokenizer2.nextToken();
          String lookupColumnId = tokenizer2.nextToken();
          mql +=
              "<selection><view>"
                  + lookupViewId
                  + "</view><column>"
                  + lookupColumnId
                  + "</column></selection>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }
      }
    }
    mql += "</mql>"; // $NON-NLS-1$
    ArrayList messages = new ArrayList();
    SimpleParameterProvider lookupParameters = new SimpleParameterProvider();
    lookupParameters.setParameter("mql", mql); // $NON-NLS-1$

    IRuntimeContext runtime =
        SolutionHelper.doAction(
            "/system/metadata/PickList.xaction",
            "lookup-list",
            lookupParameters,
            getSession(),
            messages,
            this); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
    IPentahoResultSet results = null;
    if (runtime != null) {
      if (runtime.getStatus() == IRuntimeContext.RUNTIME_STATUS_SUCCESS) {
        if (runtime.getOutputNames().contains("data")) { // $NON-NLS-1$
          results = runtime.getOutputParameter("data").getValueAsResultSet(); // $NON-NLS-1$
          Object[][] columnHeaders = results.getMetaData().getColumnHeaders();
          boolean hasColumnHeaders = columnHeaders != null;

          Element rowElement;
          Element dataElement = root.addElement("data"); // $NON-NLS-1$
          if (hasColumnHeaders) {
            for (int rowNo = 0; rowNo < columnHeaders.length; rowNo++) {
              rowElement = dataElement.addElement("COLUMN-HDR-ROW"); // $NON-NLS-1$
              for (int columnNo = 0; columnNo < columnHeaders[rowNo].length; columnNo++) {
                Object nameAttr =
                    results.getMetaData().getAttribute(rowNo, columnNo, "name"); // $NON-NLS-1$
                if ((nameAttr != null) && (nameAttr instanceof LocalizedString)) {
                  LocalizedString str = (LocalizedString) nameAttr;
                  String name = str.getLocalizedString(locale);
                  if (name != null) {
                    rowElement.addElement("COLUMN-HDR-ITEM").setText(name); // $NON-NLS-1$
                  } else {
                    rowElement
                        .addElement("COLUMN-HDR-ITEM")
                        .setText(columnHeaders[rowNo][columnNo].toString()); // $NON-NLS-1$
                  }
                } else {
                  rowElement
                      .addElement("COLUMN-HDR-ITEM")
                      .setText(columnHeaders[rowNo][columnNo].toString()); // $NON-NLS-1$
                }
              }
            }
          }
          Object[] row = results.next();
          while (row != null) {
            rowElement = dataElement.addElement("DATA-ROW"); // $NON-NLS-1$
            for (Object element : row) {
              if (element == null) {
                rowElement.addElement("DATA-ITEM").setText(""); // $NON-NLS-1$ //$NON-NLS-2$
              } else {
                rowElement.addElement("DATA-ITEM").setText(element.toString()); // $NON-NLS-1$
              }
            }
            row = results.next();
          }
        }
      }
    }

    return doc;
  }
  private Document loadModel() {
    // Create a document that describes the result
    Document doc = DocumentHelper.createDocument();

    Element root = doc.addElement("metadata"); // $NON-NLS-1$

    if (domainName == null) {
      // we can't do this without a model
      root.addElement("message")
          .setText(
              Messages.getInstance()
                  .getString(
                      "PMDUIComponent.USER_NO_DOMAIN_SPECIFIED")); //$NON-NLS-1$ //$NON-NLS-2$
      return doc;
    }

    if (modelId == null) {
      // we can't do this without a model
      root.addElement("message")
          .setText(
              Messages.getInstance()
                  .getString(
                      "PMDUIComponent.USER_NO_MODEL_SPECIFIED")); //$NON-NLS-1$ //$NON-NLS-2$
      return doc;
    }

    Element modelNode = root.addElement("model"); // $NON-NLS-1$

    // because it's lighter weight, check the thin model
    Domain domain = getMetadataRepository().getDomain(domainName);
    if (domain == null) {
      root.addElement("message")
          .setText(
              Messages.getInstance()
                  .getString(
                      "PMDUIComponent.USER_DOMAIN_LOADING_ERROR",
                      domainName)); //$NON-NLS-1$ //$NON-NLS-2$
      return doc;
    }
    String locale =
        LocaleHelper.getClosestLocale(LocaleHelper.getLocale().toString(), domain.getLocaleCodes());
    LogicalModel model = domain.findLogicalModel(modelId);

    if (model == null) {
      root.addElement("message")
          .setText(
              Messages.getInstance()
                  .getString(
                      "PMDUIComponent.USER_MODEL_LOADING_ERROR",
                      modelId)); //$NON-NLS-1$ //$NON-NLS-2$
      error(
          Messages.getInstance()
              .getString("PMDUIComponent.USER_MODEL_LOADING_ERROR", modelId)); // $NON-NLS-1$
      return doc;
    }
    modelNode.addElement("domain_id").setText(domainName); // $NON-NLS-1$
    if (model.getId() != null) {
      modelNode.addElement("model_id").setText(model.getId()); // $NON-NLS-1$
    }
    if (model.getName(locale) != null) {
      modelNode.addElement("model_name").setText(model.getName(locale)); // $NON-NLS-1$
    }
    if (model.getDescription(locale) != null) {
      modelNode
          .addElement("model_description")
          .setText(model.getDescription(locale)); // $NON-NLS-1$
    }

    Element tableNode;

    for (Category category : model.getCategories()) {
      tableNode = modelNode.addElement("view"); // $NON-NLS-1$
      if (category.getId() != null) {
        tableNode.addElement("view_id").setText(category.getId()); // $NON-NLS-1$
      }
      if (category.getName(locale) != null) {
        tableNode.addElement("view_name").setText(category.getName(locale)); // $NON-NLS-1$
      }
      if (category.getDescription(locale) != null) {
        tableNode
            .addElement("view_description")
            .setText(category.getDescription(locale)); // $NON-NLS-1$
      }
      for (LogicalColumn column : category.getLogicalColumns()) {
        Boolean hidden = (Boolean) column.getProperty("hidden"); // $NON-NLS-1$
        if (hidden != null && hidden) {
          continue;
        }
        addColumn(column, tableNode, locale);
      }
    }

    return doc;
  }