/**
 * Copyright (c) 2010 Kenn Hussey and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors: 
 *   Kenn Hussey - Initial API and implementation
 */
package org.eclipse.emf.oda.ecore.impl;

import org.eclipse.datatools.connectivity.oda.IResultSetMetaData;
import org.eclipse.datatools.connectivity.oda.OdaException;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.oda.ecore.util.StringUtil;


/**
 * Implementation of IResultSetMetaData for EMF ODA runtime driver.
 */
public abstract class ResultSetMetaData implements IResultSetMetaData
{
  /**
   * Metadata for sets of results that contain instances of classes.
   */
  protected static class EClass extends ResultSetMetaData
  {
    protected static final String FIRST_COLUMN_NAME = "@self"; //$NON-NLS-1$

    protected final org.eclipse.emf.ecore.EClass type;

    protected EClass(org.eclipse.emf.ecore.EClass type)
    {
      super();

      this.type = type;
    }

    public int getColumnCount() throws OdaException
    {
      return type.getEAllStructuralFeatures().size() + 1;
    }

    public String getColumnName(int index) throws OdaException
    {
      if (checkBounds(index) == 1)
      {
        return FIRST_COLUMN_NAME;
      }
      else
      {
        return type.getEStructuralFeature(index - 2).getName();
      }
    }

    @Override
    public String getColumnLabel(int index) throws OdaException
    {
      return checkBounds(index) > 1 ? StringUtil.getFeatureText(type.getEStructuralFeature(index - 2)) : super.getColumnLabel(index);
    }

    @Override
    protected EClassifier getColumnClassifier(int index) throws OdaException
    {
      return checkBounds(index) == 1 ? type : type.getEStructuralFeature(index - 2).getEType();
    }
  }

  /**
   * Metadata for sets of results that contain values of data types.
   */
  protected static class EDataType extends ResultSetMetaData
  {
    protected static final String FIRST_COLUMN_NAME = "@value"; //$NON-NLS-1$

    protected final org.eclipse.emf.ecore.EDataType type;

    protected EDataType(org.eclipse.emf.ecore.EDataType type)
    {
      super();

      this.type = type;
    }

    public int getColumnCount() throws OdaException
    {
      return 1;
    }

    public String getColumnName(int index) throws OdaException
    {
      return FIRST_COLUMN_NAME;
    }

    @Override
    protected EClassifier getColumnClassifier(int index) throws OdaException
    {
      return type;
    }
  }

  /**
   * Creates result set metadata of the appropriate kind, depending on whether the specified
   * type is a class or a data type.
   * @param type the type of objects result set described by the new metadata
   * @return result set metadata of the appropriate kind
   */
  public static IResultSetMetaData create(EClassifier type)
  {
    if (type instanceof org.eclipse.emf.ecore.EClass)
    {
      return new EClass((org.eclipse.emf.ecore.EClass)type);
    }
    else
    {
      return new EDataType((org.eclipse.emf.ecore.EDataType)type);
    }
  }

  protected ResultSetMetaData()
  {
    super();
  }

  /**
   * Checks that the specified index is within the appropriate bounds.
   * @param index the 1-based index
   * @return the index
   * @throws OdaException if index is not within the bounds
   */
  protected int checkBounds(int index) throws OdaException
  {
    if (index < 1 || index > getColumnCount())
    {
      throw new OdaException(new IndexOutOfBoundsException(String.valueOf(index)));
    }

    return index;
  }

  public String getColumnLabel(int index) throws OdaException
  {
    return getColumnName(index); // default
  }

  protected abstract EClassifier getColumnClassifier(int index) throws OdaException;

  public int getColumnType(int index) throws OdaException
  {
    return DataTypes.getType(getColumnClassifier(checkBounds(index)));
  }

  public String getColumnTypeName(int index) throws OdaException
  {
    return DataTypes.getTypeName(getColumnClassifier(checkBounds(index)));
  }

  public int getColumnDisplayLength(int index) throws OdaException
  {
    return DataTypes.getDisplayLength(getColumnClassifier(checkBounds(index)));
  }

  public int getPrecision(int index) throws OdaException
  {
    return DataTypes.getPrecision(getColumnClassifier(checkBounds(index)));
  }

  public int getScale(int index) throws OdaException
  {
    return DataTypes.getScale(getColumnClassifier(checkBounds(index)));
  }

  public int isNullable(int index) throws OdaException
  {
    switch (DataTypes.isNullable(getColumnClassifier(checkBounds(index))))
    {
      case DataTypes.noNulls:
        return IResultSetMetaData.columnNoNulls;
      case DataTypes.nullable:
        return IResultSetMetaData.columnNullable;
      default:
        return IResultSetMetaData.columnNullableUnknown;
    }
  }
}