All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tentackle.model.impl.IndexImpl Maven / Gradle / Ivy

The newest version!
/*
 * Tentackle - http://www.tentackle.org.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.tentackle.model.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.tentackle.common.BasicStringHelper;
import org.tentackle.model.Attribute;
import org.tentackle.model.Entity;
import org.tentackle.model.Index;
import org.tentackle.model.IndexAttribute;
import org.tentackle.model.ModelException;
import org.tentackle.model.SourceInfo;
import org.tentackle.model.parser.ConfigurationLine;
import org.tentackle.sql.Backend;

/**
 * Index descriptor.
 *
 * @author harald
 */
public class IndexImpl implements Index {

  private final Entity entity;
  private final EntityFactoryImpl factory;
  private final SourceInfo sourceInfo;
  private final List attributes;
  private ConfigurationLine sourceLine;
  private String name;
  private String comment;
  private boolean unique;
  private String filterCondition;


  /**
   * Creates an index.
   *
   * @param factory the factory to create indexes
   * @param entity the entity this index belongs to
   * @param sourceInfo the source info
   */
  public IndexImpl(EntityFactoryImpl factory, Entity entity, SourceInfo sourceInfo) {
    this.entity = entity;
    this.sourceInfo = sourceInfo;
    this.factory = factory;
    attributes = new ArrayList<>();
  }

  @Override
  public Entity getEntity() {
    return entity;
  }

  /**
   * Parses a configuration line.
   *
   * @param entity the entity
   * @param line the source line
   * @throws ModelException if parsing the model failed
   */
  public void parse(Entity entity, ConfigurationLine line) throws ModelException {

    // parse the index
    StringTokenizer stok = new StringTokenizer(line.getKey());
    boolean indexFound = false;
    boolean uniqueFound = false;
    while (stok.hasMoreTokens()) {
      String token = stok.nextToken();
      switch(token.toLowerCase()) {
        case "index":
          setSourceLine(line);
          setUnique(uniqueFound);
          indexFound = true;
          uniqueFound = false;
          break;
        case "unique":
          if (indexFound) {
            throw line.createModelException("keyword 'unique' not allowed after 'index'");
          }
          uniqueFound = true;
          break;
        default:
          if (!indexFound) {
            throw line.createModelException("unknown configuration keyword: " + token);
          }
          int ndx = token.indexOf('.');
          if (ndx > 0) {
            // cut leading schema name
            token = token.substring(ndx + 1);
          }
          setName(token);
      }
    }
    if (!indexFound) {
      throw line.createModelException("illegal configuration: " + line.getKey());
    }

    // parse the attributes
    if (entity.getAttributes().isEmpty()) {
      throw line.createModelException("index configuration line not allowed before attribute section");
    }

    String text = line.getValue();
    int ndx = text.indexOf('|');
    if (ndx > 0) {
      setFilterCondition(text.substring(ndx + 1));
      text = text.substring(0, ndx);
    }
    else {
      ndx = text.toUpperCase().indexOf("WHERE");
      if (ndx > 0) {
        setFilterCondition(text.substring(ndx + 5));
        text = text.substring(0, ndx);
      }
    }

    stok = new StringTokenizer(text, " \t,");
    IndexAttributeImpl indexAttribute = null;
    while (stok.hasMoreTokens()) {
      String token = stok.nextToken();
      if (token.toLowerCase().equals("desc")) {
        if (indexAttribute == null) {
          throw line.createModelException("keyword 'desc' must follow column name");
        }
        indexAttribute.setDescending(true);
      }
      else if (token.toLowerCase().equals("asc")) {
        if (indexAttribute == null) {
          throw line.createModelException("keyword 'asc' must follow column name");
        }
        indexAttribute.setDescending(false);
      }
      else  {
        if (indexAttribute != null) {
          getAttributes().add(indexAttribute);
          indexAttribute = null;
        }
        // short form for desc or asc is + or - prepended to the column name
        Boolean descending = null;
        if (token.startsWith("+")) {
          descending = Boolean.FALSE;
          token = token.substring(1);
        }
        if (token.startsWith("-")) {
          descending = Boolean.TRUE;
          token = token.substring(1);
        }
        // check if column name exists
        for (Attribute attribute: entity.getAttributes()) {
          if (attribute.getColumnName().equals(token)) {
            // found
            indexAttribute = factory.createIndexAttribute(this, sourceInfo);
            indexAttribute.setAttribute(attribute);
            if (descending != null) {
              indexAttribute.setDescending(descending);
            }
            break;
          }
        }
        if (indexAttribute == null) {
          // not found
          throw line.createModelException("undefined column name: " + token);
        }
      }
    }
    if (indexAttribute != null) {
      getAttributes().add(indexAttribute);
    }
  }



  @Override
  public String getName() {
    return name;
  }

  @Override
  public String getName(Entity entity) {
    return entity.getTableNameWithoutSchema() + "_" + name;
  }

  @Override
  public String getComment() {
    return comment;
  }

  @Override
  public boolean isUnique() {
    return unique;
  }

  @Override
  public String getFilterCondition() {
    return filterCondition;
  }

  @Override
  public List getAttributes() {
    return attributes;
  }


  public void setName(String name) {
    this.name = BasicStringHelper.toLower(name);
  }

  public void setComment(String comment) {
    this.comment = comment;
  }

  public void setUnique(boolean unique) {
    this.unique = unique;
  }

  public void setFilterCondition(String filterCondition) {
    if (BasicStringHelper.isAllWhitespace(filterCondition)) {
      this.filterCondition = null;
    }
    else  {
      this.filterCondition = filterCondition.trim();
    }
  }

  @Override
  public String toString() {
    return getName();
  }


  @Override
  public void validate() throws ModelException {
    if (BasicStringHelper.isAllWhitespace(getName())) {
      throw createModelException("missing index name");
    }
    // leading underscore is ok, because index name gets the tablename prepended

    for (Backend backend: factory.getBackends()) {
      try {
        backend.assertValidName("index name", getName(entity));
      }
      catch (RuntimeException rex) {
        throw new ModelException(rex.getMessage(), sourceInfo, rex);
      }
    }

    if (getAttributes().isEmpty()) {
      throw createModelException("index " + this + " does not contain any attributes");
    }
  }

  @Override
  public ConfigurationLine getSourceLine() {
    return sourceLine;
  }

  public void setSourceLine(ConfigurationLine sourceLine) {
    this.sourceLine = sourceLine;
  }

  /**
   * Creates a model exception.
   * 

* Refers to the source line if set, otherwise just the message. * * @param message the message * @return the exception */ public ModelException createModelException(String message) { ModelException ex; if (sourceLine != null) { ex = sourceLine.createModelException(message); } else { ex = new ModelException(message, entity); } return ex; } @Override public String sqlCreateIndex(Backend backend, Entity entity) { StringBuilder buf = new StringBuilder(); if (getComment() != null) { buf.append("-- "); buf.append(getComment()); buf.append('\n'); } String[] columnNames = new String[getAttributes().size()]; int attributeCount = 0; for (IndexAttribute indexAttribute : getAttributes()) { if (indexAttribute.isDescending()) { columnNames[attributeCount] = "-" + indexAttribute.getAttribute().getColumnName(); } else { columnNames[attributeCount] = indexAttribute.getAttribute().getColumnName(); } attributeCount++; } buf.append(backend.sqlCreateIndex(entity.getTableName(), getName(entity), isUnique(), getFilterCondition(), columnNames)); return buf.toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy