com.odoo.orm.OModel.java Source code

Java tutorial

Introduction

Here is the source code for com.odoo.orm.OModel.java

Source

/*
 * Odoo, Open Source Management Solution
 * Copyright (C) 2012-today Odoo SA (<http:www.odoo.com>)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version
 * 
 * This program 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 Affero General Public License for more details
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http:www.gnu.org/licenses/>
 * 
 */
package com.odoo.orm;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import odoo.ODomain;
import odoo.OdooVersion;

import org.json.JSONObject;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;

import com.odoo.App;
import com.odoo.orm.OColumn.RelationType;
import com.odoo.orm.ORelIds.RelData;
import com.odoo.orm.annotations.Odoo;
import com.odoo.orm.annotations.Odoo.Functional;
import com.odoo.orm.sql.OQuery;
import com.odoo.orm.sql.OQuery.QueryType;
import com.odoo.orm.types.OBoolean;
import com.odoo.orm.types.ODateTime;
import com.odoo.orm.types.OInteger;
import com.odoo.orm.types.OText;
import com.odoo.orm.types.OVarchar;
import com.odoo.receivers.DataSetChangeReceiver;
import com.odoo.support.OUser;
import com.odoo.support.provider.OContentProvider;
import com.odoo.support.provider.OContentProviderHelper;
import com.odoo.util.ODate;
import com.odoo.util.PreferenceManager;
import com.odoo.util.StringUtils;
import com.odoo.util.logger.OLog;

/**
 * The Class OModel.
 */
public class OModel extends OSQLiteHelper implements OModelHelper {

    /** The Constant TAG. */
    public static final String TAG = OModel.class.getSimpleName();

    /** The context. */
    private Context mContext = null;

    /** The _name. */
    private String _name = null;

    /** The columns. */
    private List<OColumn> mColumns = new ArrayList<OColumn>();

    /** The functional columns. */
    private List<OColumn> mFunctionalColumns = new ArrayList<OColumn>();
    private List<OColumn> mRelationColumns = new ArrayList<OColumn>();

    /** The user. */
    private OUser mUser = null;

    /** The sync helper. */
    private OSyncHelper mSyncHelper = null;

    /** The syncing data. */
    private Boolean mSyncingData = false;

    /** The check in active record. */
    private Boolean mCheckInActiveRecord = false;

    /** The app. */
    private App mApp = null;

    /** The odoo version. */
    private OdooVersion mOdooVersion = null;

    /** The offset. */
    private Integer mOffset = 0;

    /** The limit. */
    private Integer mLimit = -1;

    /** The with functional columns. */
    private Boolean mWithFunctionalColumns = false;

    /**
     * The Enum Command.
     */
    public enum Command {

        /** The Add. */
        Add,
        /** The Update. */
        Update,
        /** The Delete. */
        Delete,
        /** The Replace. */
        Replace
    }

    // Server Base Columns
    /** The id. */
    OColumn id = new OColumn("ID", OInteger.class).setDefault(0);

    /** The create_date. */
    @Odoo.api.v8
    @Odoo.api.v9alpha
    public OColumn create_date = new OColumn("Created On", ODateTime.class).setParsePattern(ODate.DEFAULT_FORMAT);

    /** The write_date. */
    @Odoo.api.v8
    @Odoo.api.v9alpha
    public OColumn write_date = new OColumn("Last Updated On", ODateTime.class)
            .setParsePattern(ODate.DEFAULT_FORMAT);

    // Local Base Columns
    /** The _id. */
    OColumn _id = new OColumn("Local ID", OInteger.class).setAutoIncrement(true).setLocalColumn();

    /** The odoo_name. */
    OColumn odoo_name = new OColumn("Odoo Account Name", OVarchar.class, 100).setRequired(true).setLocalColumn();

    /** The local_write_date. */
    OColumn local_write_date = new OColumn("Local Write Date", ODateTime.class).setLocalColumn();

    /** The is_dirty. */
    OColumn is_dirty = new OColumn("Dirty Row", OText.class).setDefault(false).setLocalColumn();

    /** The is_active. */
    OColumn is_active = new OColumn("Row Active", OBoolean.class).setDefault(true).setLocalColumn();

    /** The declared fields. */
    private HashMap<String, Field> mDeclaredFields = new HashMap<String, Field>();

    public static String getSyncUserDB(Context context) {
        OUser user = OModel.getSyncUser(context);
        if (user != null) {
            return user.getDBName();
        }
        return "";
    }

    public static OUser getSyncUser(Context context) {
        App app = (App) context.getApplicationContext();
        OUser user = app.getSyncUser();
        if (user == null) {
            user = OUser.current(context);
        }
        return user;
    }

    /**
     * Instantiates a new o model.
     * 
     * @param context
     *            the context
     * @param model_name
     *            the model_name
     */
    public OModel(Context context, String model_name) {
        super(context, OModel.getSyncUserDB(context));
        mContext = context;
        _name = model_name;
        mUser = OUser.current(mContext);
        mApp = (App) context.getApplicationContext();
        if (mUser != null) {
            mOdooVersion = new OdooVersion();
            mOdooVersion.setVersion_number(mUser.getVersion_number());
            mOdooVersion.setServer_serie(mUser.getVersion_serie());
        } else {
            mApp.createInstance();
            mOdooVersion = mApp.getOdooVersion();
        }
        createFieldList();
    }

    public OModel newInstance(OModel model) {
        try {
            return model.getClass().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public OContentProvider getContentProvider() {
        return null;
    }

    /**
     * Generate content Uri.
     * 
     * @return the uri
     */
    public Uri uri() {
        return OContentProvider.buildURI(authority(), path());
    }

    public String path() {
        OContentProviderHelper provider = (OContentProviderHelper) getContentProvider();
        if (provider != null)
            return provider.path();
        else
            OLog.log("Override getContentProvider() method in " + getClass().getSimpleName());
        return null;
    }

    /**
     * Generate content Authority.
     * 
     * @return the string
     */
    public String authority() {
        OContentProviderHelper provider = (OContentProviderHelper) getContentProvider();
        if (provider != null)
            return provider.authority();
        else
            OLog.log("Override getContentProvider() method in " + getClass().getSimpleName());
        return null;
    }

    /**
     * Sets the user.
     * 
     * @param user
     *            the new user
     */
    public void setUser(OUser user) {
        mUser = user;
    }

    public OUser getUser() {
        return mUser;
    }

    /**
     * Gets the model name.
     * 
     * @return the model name
     */
    public String getModelName() {
        return _name;
    }

    /**
     * Gets the table name.
     * 
     * @return the table name
     */
    public String getTableName() {
        return _name.replaceAll("\\.", "_");
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.odoo.orm.OModelHelper#getColumns()
     */
    public List<OColumn> getColumns() {
        return getColumns(null);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.odoo.orm.OModelHelper#getColumns(java.lang.Boolean)
     */
    @Override
    public List<OColumn> getColumns(Boolean local) {
        if (mColumns.size() <= 0) {
            prepareColumns();
        }
        if (local != null) {
            List<OColumn> cols = new ArrayList<OColumn>();
            for (OColumn column : getColumns())
                if (local == column.isLocal())
                    cols.add(column);
            return cols;
        } else {
            return mColumns;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.odoo.orm.OModelHelper#defaultDomain()
     */
    @Override
    public ODomain defaultDomain() {
        return new ODomain();
    }

    /**
     * Gets the many to many columns.
     * 
     * @param relation_model
     *            the relation_model
     * @return the many to many columns
     */
    public List<OColumn> getManyToManyColumns(OModel relation_model) {
        List<OColumn> cols = new ArrayList<OColumn>();
        odoo_name.setName("odoo_name");
        cols.add(odoo_name);
        local_write_date.setName("local_write_date");
        cols.add(local_write_date);
        is_dirty.setName("is_dirty");
        cols.add(is_dirty);
        is_active.setName("is_active");
        cols.add(is_active);

        OColumn base_id = new OColumn("Base Id", OInteger.class);
        base_id.setName(getTableName() + "_id");
        cols.add(base_id);
        OColumn relation_id = new OColumn("Relation Id", OInteger.class);
        relation_id.setName(relation_model.getTableName() + "_id");
        cols.add(relation_id);
        return cols;
    }

    private void createFieldList() {
        try {
            List<Field> fields = new ArrayList<Field>();
            fields.addAll(Arrays.asList(getClass().getSuperclass().getDeclaredFields()));
            fields.addAll(Arrays.asList(getClass().getDeclaredFields()));
            mDeclaredFields.clear();
            for (Field field : fields) {
                if (field.getType().isAssignableFrom(OColumn.class)) {
                    mDeclaredFields.put(field.getName(), field);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Prepare columns.
     */
    public void prepareColumns() {
        if (mDeclaredFields.size() == 0)
            createFieldList();
        for (String key : mDeclaredFields.keySet()) {
            OColumn column = getColumn(key);
            if (column != null) {
                if (column.getRelationType() != null) {
                    mRelationColumns.add(column);
                }
                if (column.isFunctionalColumn()) {
                    if (column.canFunctionalStore()) {
                        mColumns.add(column);
                    }
                    mFunctionalColumns.add(column);
                } else {
                    mColumns.add(column);
                }
            }
        }
    }

    public List<OColumn> getRelationColumns() {
        return mRelationColumns;
    }

    public List<OColumn> getFunctionalColumns() {
        return mFunctionalColumns;
    }

    /**
     * Gets the column.
     * 
     * @param name
     *            the name
     * @return the column
     */
    public OColumn getColumn(String name) {
        OColumn column = null;
        try {
            Field field = mDeclaredFields.get(name);
            if (field != null) {
                field.setAccessible(true);
                column = (OColumn) field.get(this);
                column.setName(field.getName());
                Boolean validField = (column.isAccessible()) ? validateFieldVersion(field) : true;
                if (validField) {
                    Method method = checkForFunctionalColumn(field);
                    column.setName(name);
                    if (method != null) {
                        column.setFunctionalMethod(method);
                        column.setFunctionalStore(checkForFunctionalStore(field));
                        column.checkRowId(checkForFunctionalRowIdCheck(field));
                        if (column.canFunctionalStore()) {
                            column.setFunctionalStoreDepends(getFunctionalDepends(field));
                        } else {
                            column.setLocalColumn();
                        }
                    }
                    // Check for onChange Column
                    Method onChangeMethod = checkForOnChangeMethod(field);
                    if (onChangeMethod != null) {
                        column.setOnChangeMethod(onChangeMethod);
                        column.setOnChangeBGProcess(checkForOnChangeBGProcess(field));
                    }

                    // Check for domain Filter on Column
                    column.setHasDomainFilterColumn(isDomainFilterColumn(field));
                } else
                    return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return column;
    }

    public OColumn getPermReadColumn(String name) {
        OColumn column = null;
        try {
            Field field = getClass().getSuperclass().getDeclaredField(name);
            if (field != null) {
                field.setAccessible(true);
                if (validateFieldVersion(field)) {
                    Method method = checkForFunctionalColumn(field);
                    column = (OColumn) field.get(this);
                    column.setName(name);
                    if (method != null) {
                        column.setFunctionalMethod(method);
                        column.setFunctionalStore(checkForFunctionalStore(field));
                        if (column.canFunctionalStore()) {
                            column.setFunctionalStoreDepends(getFunctionalDepends(field));
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return column;
    }

    /**
     * Check for functional column.
     * 
     * @param field
     *            the field
     * @return the method
     */
    private Method checkForFunctionalColumn(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.Functional.class);
        if (annotation != null) {
            Odoo.Functional functional = (Functional) annotation;
            String method_name = functional.method();
            try {
                if (functional.store())
                    return getClass().getMethod(method_name, OValues.class);
                else
                    return getClass().getMethod(method_name, ODataRow.class);
            } catch (NoSuchMethodException e) {
                Log.e(TAG, "No Such Method: " + e.getMessage());
            }
        }
        return null;
    }

    private boolean isDomainFilterColumn(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.hasDomainFilter.class);
        if (annotation != null) {
            Odoo.hasDomainFilter domainFilter = (Odoo.hasDomainFilter) annotation;
            return domainFilter.checkDomainRuntime();
        }
        return false;
    }

    private Boolean checkForOnChangeBGProcess(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.onChange.class);
        if (annotation != null) {
            Odoo.onChange onChange = (Odoo.onChange) annotation;
            return onChange.bg_process();
        }
        return false;
    }

    private Method checkForOnChangeMethod(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.onChange.class);
        if (annotation != null) {
            Odoo.onChange onChange = (Odoo.onChange) annotation;
            String method_name = onChange.method();
            try {
                return getClass().getMethod(method_name, ODataRow.class);
            } catch (NoSuchMethodException e) {
                Log.e(TAG, "No Such Method: " + e.getMessage());
            }
        }
        return null;
    }

    /**
     * Check for functional store.
     * 
     * @param field
     *            the field
     * @return the boolean
     */
    public Boolean checkForFunctionalStore(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.Functional.class);
        if (annotation != null) {
            Odoo.Functional functional = (Functional) annotation;
            return functional.store();
        }
        return false;
    }

    /**
     * Check for functional row id check.
     * 
     * @param field
     *            the field
     * @return the boolean
     */
    private Boolean checkForFunctionalRowIdCheck(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.Functional.class);
        if (annotation != null) {
            Odoo.Functional functional = (Functional) annotation;
            return functional.checkRowId();
        }
        return true;
    }

    /**
     * Gets the functional depends.
     * 
     * @param field
     *            the field
     * @return the functional depends
     */
    public String[] getFunctionalDepends(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.Functional.class);
        if (annotation != null) {
            Odoo.Functional functional = (Functional) annotation;
            if (functional.store()) {
                return functional.depends();
            }
        }
        return null;
    }

    public OdooVersion getOdooVersion() {
        return mOdooVersion;
    }

    private Boolean validateFieldVersion(Field field) {
        if (mOdooVersion != null) {
            Annotation[] annotations = field.getAnnotations();
            if (annotations.length > 0) {
                int versions = 0;
                for (Annotation annotation : annotations) {
                    if (annotation.annotationType().getDeclaringClass().isAssignableFrom(Odoo.api.class)) {
                        switch (mOdooVersion.getVersion_number()) {
                        case 9: // Checks for v9
                            if (annotation.annotationType().isAssignableFrom(Odoo.api.v9alpha.class)) {
                                versions++;
                            }
                            break;
                        case 8: // Checks for v8
                            if (annotation.annotationType().isAssignableFrom(Odoo.api.v8.class)) {
                                versions++;
                            }
                            break;
                        case 7: // Checks for v7
                            if (annotation.annotationType().isAssignableFrom(Odoo.api.v7.class)) {
                                versions++;
                            }
                            break;
                        }
                    }
                    Class<? extends Annotation> type = annotation.annotationType();
                    if (type.isAssignableFrom(Odoo.Functional.class) || type.isAssignableFrom(Odoo.onChange.class)
                            || type.isAssignableFrom(Odoo.hasDomainFilter.class)) {
                        versions++;
                    }
                }
                return (versions > 0) ? true : false;
            }
            return true;
        }
        return false;
    }

    /**
     * Gets the functional method value.
     * 
     * @param column
     *            the column
     * @param record
     *            the record
     * @return the functional method value
     */
    public Object getFunctionalMethodValue(OColumn column, Object record) {
        if (column.isFunctionalColumn()) {
            Method method = column.getMethod();
            OModel model = this;
            try {
                return method.invoke(model, new Object[] { record });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    public ODataRow getOnChangeValue(OColumn column, ODataRow row) {
        if (column.hasOnChange()) {
            Method method = column.getOnChangeMethod();
            OModel model = this;
            try {
                return (ODataRow) method.invoke(model, new Object[] { row });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * Creates the instance.
     * 
     * @param model_class
     *            the model_class
     * @return the o model
     */
    public OModel createInstance(Class<?> model_class) {
        try {
            Constructor<?> constr = model_class.getConstructor(Context.class);
            OModel model = (OModel) constr.newInstance(new Object[] { mContext });
            return model;
        } catch (Exception e) {
            Log.d(TAG, model_class.getSimpleName());
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Generate columns string array used as Projection in ContentProvider.
     * 
     * @return the string[]
     */
    public String[] projection() {
        List<String> projection = new ArrayList<String>();
        for (OColumn col : getColumns()) {
            if (col.getRelationType() == null
                    || (col.getRelationType() != null && col.getRelationType() == RelationType.ManyToOne)) {
                projection.add(col.getName());
            }
        }
        return projection.toArray(new String[projection.size()]);
    }

    public String[] fields() {
        List<String> fields = new ArrayList<String>();
        for (OColumn col : getColumns(false)) {
            fields.add(col.getName());
        }
        return fields.toArray(new String[fields.size()]);
    }

    /**
     * Select.
     * 
     * @return the list
     */
    public List<ODataRow> select() {
        return select(null, null, null, null, null);
    }

    /**
     * Select.
     * 
     * @param id
     *            the id
     * @return the o data row
     */
    public ODataRow select(Integer id) {
        String selection = OColumn.ROW_ID + " = ?";
        List<ODataRow> records = select(selection, new Object[] { id }, null, null, null);
        if (records.size() > 0)
            return records.get(0);
        return null;
    }

    public Integer selectRowId(Integer server_id) {
        List<ODataRow> records = select("id = ? ", new Object[] { server_id });
        if (records.size() > 0) {
            return records.get(0).getInt(OColumn.ROW_ID);
        }
        return null;
    }

    public Integer selectServerId(Integer row_id) {
        List<ODataRow> records = browse().columns("id", OColumn.ROW_ID, "name")
                .addWhere(OColumn.ROW_ID, "=", row_id).fetch();
        if (records.size() > 0) {
            return records.get(0).getInt("id");
        }
        return null;
    }

    /**
     * Select.
     * 
     * @param where
     *            the where
     * @param args
     *            the args
     * @return the list
     */
    public List<ODataRow> select(String where, Object[] args) {
        return select(where, args, null, null, null);
    }

    /**
     * Select.
     * 
     * @param where
     *            the where
     * @param whereArgs
     *            the where args
     * @param groupBy
     *            the group by
     * @param having
     *            the having
     * @param orderBy
     *            the order by
     * @return the list
     */
    public List<ODataRow> select(String where, Object[] whereArgs, String groupBy, String having, String orderBy) {
        List<ODataRow> records = new ArrayList<ODataRow>();
        SQLiteDatabase db = getReadableDatabase();
        String limit = null;
        if (mLimit > 0) {
            limit = mOffset + ", " + mLimit;
        }
        Cursor cr = db.query(getTableName(), new String[] { "*" }, getWhereClause(where),
                getWhereArgs(where, whereArgs), groupBy, having, orderBy, limit);
        if (cr.moveToFirst()) {
            do {
                ODataRow row = new ODataRow();
                for (OColumn col : getColumns()) {
                    if (col.getRelationType() == null) {
                        row.put(col.getName(), createRecordRow(col, cr));
                    } else {
                        switch (col.getRelationType()) {
                        case ManyToMany:
                            row.put(col.getName(),
                                    new OM2MRecord(this, col, cr.getInt(cr.getColumnIndex(OColumn.ROW_ID))));
                            break;
                        case OneToMany:
                            row.put(col.getName(),
                                    new OO2MRecord(this, col, cr.getInt(cr.getColumnIndex(OColumn.ROW_ID))));
                            break;
                        case ManyToOne:
                            row.put(col.getName(),
                                    new OM2ORecord(this, col, cr.getInt(cr.getColumnIndex(col.getName()))));
                            break;
                        }
                    }
                }
                /*
                 * Adding functional column values to record values if not
                 * syncing
                 */
                if (!mSyncingData) {
                    for (OColumn col : mFunctionalColumns) {
                        if (!col.canFunctionalStore()) {
                            row.put(col.getName(), getFunctionalMethodValue(col, row));
                        }
                    }
                }
                if (row.getInt("id") == 0 || row.getString("id").equals("false"))
                    row.put("id", 0);
                records.add(row);
            } while (cr.moveToNext());
        }
        cr.close();
        db.close();
        return records;
    }

    public List<ODataRow> query(String sql, String[] args) {
        return query(sql, args, true);
    }

    public List<ODataRow> query(String sql, String[] args, Boolean closeConnection) {
        List<ODataRow> records = new ArrayList<ODataRow>();
        SQLiteDatabase db = getReadableDatabase();
        Cursor cr = db.rawQuery(sql, args);
        if (cr.moveToFirst()) {
            do {
                ODataRow row = createRowFromCursor(cr);
                if (mWithFunctionalColumns) {
                    for (OColumn col : mFunctionalColumns) {
                        if (!col.canFunctionalStore()) {
                            row.put(col.getName(), getFunctionalMethodValue(col, row));
                        }
                    }
                }
                records.add(row);
            } while (cr.moveToNext());
        }
        cr.close();
        if (closeConnection)
            db.close();
        return records;
    }

    public ODataRow selectRelRecord(String[] columns, int base_id) {
        ODataRow row = new ODataRow();
        for (String col : columns) {
            OColumn column = getColumn(col);
            if (column.getRelationType() != null) {
                switch (column.getRelationType()) {
                case ManyToMany:
                    row.put(column.getName(), new OM2MRecord(this, column, base_id));
                    break;
                case OneToMany:
                    row.put(column.getName(), new OO2MRecord(this, column, base_id));
                    break;
                case ManyToOne:
                    row.put(column.getName(), new OM2ORecord(this, column, base_id));
                    break;
                }
            }
        }
        return row;
    }

    public OModel withFunctionalColumns() {
        return withFunctionalColumns(true);
    }

    public OModel withFunctionalColumns(Boolean withFunctionalColumns) {
        mWithFunctionalColumns = withFunctionalColumns;
        if (withFunctionalColumns) {
            prepareColumns();
        }
        return this;
    }

    public ODataRow createRowFromCursor(Cursor cr) {
        ODataRow row = new ODataRow();
        for (String col_name : cr.getColumnNames()) {
            OColumn col = new OColumn(col_name).setDefault(false);
            col.setName(col_name);
            row.put(col_name, createRecordRow(col, cr));
        }
        if (row.getInt("id") == 0 || row.getString("id").equals("false"))
            row.put("id", 0);
        return row;
    }

    /**
     * Gets the name.
     * 
     * @param model
     *            the model
     * @param row_id
     *            the row_id
     * @return the name
     */
    public String getName(int row_id) {
        String name = "false";
        if (getColumn("name") != null) {
            SQLiteDatabase db = getReadableDatabase();
            Cursor cr = db.query(getTableName(), new String[] { "name" }, OColumn.ROW_ID + " = ?",
                    new String[] { row_id + "" }, null, null, null);
            if (cr.moveToFirst()) {
                name = cr.getString(cr.getColumnIndex("name"));
            }
            cr.close();
            db.close();
        }
        return name;
    }

    /**
     * Select one to many rel ids.
     * 
     * @param base
     *            the base
     * @param rel
     *            the rel
     * @param base_id
     *            the base_id
     * @param ref_column
     *            the ref_column
     * @return the list
     */
    public List<Integer> selecto2MRelIds(OModel base, OModel rel, int base_id, String ref_column) {
        List<Integer> ids = new ArrayList<Integer>();
        SQLiteDatabase db = getReadableDatabase();
        Cursor cr = db.query(rel.getTableName(), new String[] { "id" }, ref_column + " = ?",
                new String[] { base_id + "" }, null, null, null);
        if (cr.moveToFirst()) {
            do {
                ids.add(cr.getInt(cr.getColumnIndex("id")));
            } while (cr.moveToNext());
        }
        cr.close();
        db.close();
        return ids;
    }

    /**
     * Select many to many rel ids.
     * 
     * @param base
     *            the base
     * @param rel
     *            the rel
     * @param base_id
     *            the base_id
     * @return the list
     */
    public List<Integer> selectM2MRelIds(OModel base, OModel rel, int base_id) {
        List<Integer> ids = new ArrayList<Integer>();
        String table = base.getTableName() + "_" + rel.getTableName() + "_rel";
        String base_col = base.getTableName() + "_id";
        String rel_col = rel.getTableName() + "_id";
        SQLiteDatabase db = getReadableDatabase();
        String where = base_col + " = ? and odoo_name = ?";
        Object[] whereArgs = new Object[] { base_id, mUser.getAndroidName() };
        Cursor cr = db.query(table, new String[] { "*" }, getWhereClause(where), getWhereArgs(where, whereArgs),
                null, null, null);
        if (cr.moveToFirst()) {
            do {
                int rel_id = cr.getInt(cr.getColumnIndex(rel_col));
                if (rel.count(OColumn.ROW_ID + " = ?", new Object[] { rel_id }) > 0)
                    ids.add(rel_id);
            } while (cr.moveToNext());
        }
        cr.close();
        db.close();
        return ids;
    }

    /**
     * Select m2 m records.
     * 
     * @param base
     *            the base
     * @param rel
     *            the rel
     * @param base_id
     *            the base_id
     * @return the list
     */
    public List<ODataRow> selectM2MRecords(OModel base, OModel rel, int base_id) {
        List<ODataRow> records = new ArrayList<ODataRow>();
        String table = base.getTableName() + "_" + rel.getTableName() + "_rel";
        String base_col = base.getTableName() + "_id";
        String rel_col = rel.getTableName() + "_id";
        SQLiteDatabase db = getReadableDatabase();
        String where = base_col + " = ? and odoo_name = ?";
        Object[] whereArgs = new Object[] { base_id, mUser.getAndroidName() };
        Cursor cr = db.query(table, new String[] { "*" }, getWhereClause(where), getWhereArgs(where, whereArgs),
                null, null, null);
        List<Integer> ids = new ArrayList<Integer>();
        if (cr.moveToFirst()) {
            do {
                int rel_id = cr.getInt(cr.getColumnIndex(rel_col));
                ids.add(rel_id);
            } while (cr.moveToNext());
        }
        cr.close();
        db.close();
        records.addAll(rel.select(OColumn.ROW_ID + " IN (" + StringUtils.repeat(" ?, ", ids.size() - 1) + "?)",
                new Object[] { ids }));
        return records;
    }

    /**
     * Creates the record row.
     * 
     * @param column
     *            the column
     * @param cr
     *            the cr
     * @return the object
     */
    public Object createRecordRow(OColumn column, Cursor cr) {
        Object value = false;
        if (column.getDefaultValue() != null) {
            value = column.getDefaultValue();
        }
        int index = cr.getColumnIndex(column.getName());
        switch (cr.getType(index)) {
        case Cursor.FIELD_TYPE_NULL:
            value = false;
            break;
        case Cursor.FIELD_TYPE_STRING:
            value = cr.getString(index);
            break;
        case Cursor.FIELD_TYPE_INTEGER:
            value = cr.getInt(index);
            break;
        case Cursor.FIELD_TYPE_FLOAT:
            value = cr.getFloat(index);
            break;
        case Cursor.FIELD_TYPE_BLOB:
            value = cr.getBlob(index);
            break;
        }
        return value;
    }

    /**
     * Truncate.
     * 
     * @return true, if successful
     */
    public boolean truncate() {
        delete(null, null);
        return true;
    }

    /**
     * Count.
     * 
     * @return the int
     */
    public int count() {
        return count(null, null);
    }

    /**
     * Count.
     * 
     * @param where
     *            the where
     * @param whereArgs
     *            the where args
     * @return the int
     */
    public int count(String where, Object[] whereArgs) {

        int count = 0;
        SQLiteDatabase db = getReadableDatabase();
        String whr = getWhereClause(where);
        String[] args = getWhereArgs(whr, whereArgs);
        Cursor cr = db.query(getTableName(), new String[] { "count(*) as total" }, whr, args, null, null, null);
        cr.moveToFirst();
        count = cr.getInt(0);
        cr.close();
        db.close();
        return count;
    }

    /**
     * Creates the or replace.
     * 
     * @param values
     *            the values
     * @return the integer
     */
    public Integer createORReplace(OValues values) {
        List<OValues> vals = new ArrayList<OValues>();
        vals.add(values);
        return createORReplace(vals).get(0);
    }

    /**
     * Creates the or replace.
     * 
     * @param values_list
     *            the values_list
     * @return the list
     */
    public List<Integer> createORReplace(List<OValues> values_list) {
        List<Integer> ids = new ArrayList<Integer>();
        for (OValues values : values_list) {
            if (!hasRecord(values.getInt("id")))
                ids.add(create(values));
            else {
                ids.add(selectRowId(values.getInt("id")));
                values.put(OColumn.ROW_ID, selectRowId(values.getInt("id")));
                update(values, "id = ?", new Object[] { values.getInt("id") });
            }
        }
        return ids;
    }

    /**
     * Checks for record with server 'id' column.
     * 
     * @param id
     *            the id
     * @return true, if successful
     */
    public boolean hasRecord(int id) {
        if (count("id = ? ", new Object[] { id }) > 0)
            return true;
        return false;
    }

    /**
     * Creates the.
     * 
     * @param values
     *            the values
     * @return the int
     */
    public int create(OValues values) {
        if (!values.contains("odoo_name")) {
            values.put("odoo_name", mUser.getAndroidName());
        }
        ContentValues vals = createValues(values);
        SQLiteDatabase db = getWritableDatabase();
        db.insert(getTableName(), null, vals);
        db.close();
        int newId = getCreateId();
        values.put(OColumn.ROW_ID, newId);
        updateRelationColumns(values);
        sendDatasetChangeBroadcast(newId);
        notifyDataChange(newId);
        return newId;
    }

    /**
     * Update relation columns values for ManyToMany and OneToMany
     * 
     * @param vals
     * @param newId
     * @return
     */
    @SuppressWarnings("unchecked")
    private boolean updateRelationColumns(OValues values) {
        SQLiteDatabase db = getWritableDatabase();
        for (OColumn column : getColumns()) {
            if (column.getRelationType() != null && values.contains(column.getName())) {
                switch (column.getRelationType()) {
                case ManyToMany:
                    OModel rel_model = createInstance(column.getType());
                    Command command = Command.Replace;
                    List<Integer> rel_ids = new ArrayList<Integer>();
                    if (values.get(column.getName()) instanceof ORelIds) {
                        ORelIds rel_objs = (ORelIds) values.get(column.getName());
                        Integer base_id = rel_objs.getBaseId();
                        if (base_id == null) {
                            base_id = values.getInt(OColumn.ROW_ID);
                        }
                        for (String key : rel_objs.keys()) {
                            RelData data = rel_objs.get(key);
                            command = data.getCommand();
                            rel_ids = new ArrayList<Integer>();
                            rel_ids.addAll(data.getIds());
                            if (command == Command.Add) {
                                manageManyToManyRecords(db, rel_model, rel_ids, base_id, Command.Replace);
                            }
                        }
                    } else {
                        rel_ids = (List<Integer>) values.get(column.getName());
                        if (rel_ids == null) {
                            rel_ids = new ArrayList<Integer>();
                        }
                        manageManyToManyRecords(db, rel_model, rel_ids, values.getInt(OColumn.ROW_ID), command);
                    }
                    break;
                case OneToMany:
                    rel_model = createInstance(column.getType());
                    command = Command.Replace;
                    Integer rec_id = 0;
                    if (values.get(column.getName()) instanceof ORelIds) {
                        ORelIds rel_objs = (ORelIds) values.get(column.getName());
                        rec_id = rel_objs.getBaseId();
                        if (rec_id == null) {
                            rec_id = values.getInt(OColumn.ROW_ID);
                        }
                        for (String key : rel_objs.keys()) {
                            RelData data = rel_objs.get(key);
                            command = data.getCommand();
                            rel_ids = new ArrayList<Integer>();
                            rel_ids.addAll(data.getIds());
                            OValues rel_vals = new OValues();
                            switch (command) {
                            case Add:
                                rel_vals.put(column.getRelatedColumn(), rec_id);
                                break;
                            case Delete:
                                rel_vals.put(column.getRelatedColumn(), false);
                                break;
                            default:
                                break;
                            }
                            String whr = OColumn.ROW_ID + " IN (" + StringUtils.repeat(" ?, ", rel_ids.size() - 1)
                                    + "?)";
                            Object[] args = new Object[] { rel_ids };
                            if (rel_ids.size() > 0)
                                rel_model.update(rel_vals, whr, args);
                        }
                    }
                    break;
                default:
                    break;
                }
            }
        }
        db.close();
        return true;
    }

    private void notifyDataChange(Integer id) {
        // FIXME: remove method after content provider ready
        /*
         * Uri uri = uri(); if (id != null) uri.buildUpon().appendPath(id + "");
         * mContext.getContentResolver().notifyChange(uri, null, false);
         */
    }

    private void sendDatasetChangeBroadcast(Integer newId) {
        Intent intent = new Intent();
        intent.setAction(DataSetChangeReceiver.DATA_CHANGED);
        intent.putExtra("model", getModelName());
        intent.putExtra("id", newId);
        mContext.sendBroadcast(intent);
    }

    /**
     * Gets the creates the id.
     * 
     * @return the creates the id
     */
    private int getCreateId() {
        int newId = 0;
        SQLiteDatabase db = getReadableDatabase();
        Cursor cr = db.query("sqlite_sequence", new String[] { "name", "seq" }, "name = ?",
                new String[] { getTableName() }, null, null, null);
        if (cr.moveToFirst()) {
            newId = cr.getInt(cr.getColumnIndex("seq"));
        }
        cr.close();
        db.close();
        return newId;
    }

    /**
     * Update.
     * 
     * @param values
     *            the values
     * @param id
     *            the id
     * @return the int
     */
    public int update(OValues values, Integer id) {
        return update(values, OColumn.ROW_ID + " = ? ", new Object[] { id });
    }

    /**
     * Update.
     * 
     * @param updateValues
     *            the update values
     * @param where
     *            the where
     * @param whereArgs
     *            the where args
     * @return the int
     */
    public int update(OValues updateValues, String where, Object[] whereArgs) {
        int affectedRows = 0;
        ContentValues values = createValues(updateValues);
        SQLiteDatabase db = getWritableDatabase();
        if (!updateValues.contains("is_dirty"))
            values.put("is_dirty", "true");
        affectedRows = db.update(getTableName(), values, getWhereClause(where), getWhereArgs(where, whereArgs));
        db.close();
        updateRelationColumns(updateValues);
        notifyDataChange(null);
        return affectedRows;
    }

    /**
     * Delete.
     * 
     * @param id
     *            the id
     * @return true, if successful
     */
    public boolean delete(int id) {
        return delete(OColumn.ROW_ID + " = ? ", new Object[] { id });
    }

    /**
     * Delete.
     * 
     * @param where
     *            the where
     * @param whereArgs
     *            the where args
     * @return true, if successful
     */
    public boolean delete(String where, Object[] whereArgs) {
        return delete(where, whereArgs, false);
    }

    /**
     * Delete.
     * 
     * @param where
     *            the where
     * @param whereArgs
     *            the where args
     * @param removeFromLocal
     *            the remove from local
     * @return true, if successful
     */
    public boolean delete(String where, Object[] whereArgs, boolean removeFromLocal) {
        Boolean deleted = false;
        if (removeFromLocal) {
            SQLiteDatabase db = getWritableDatabase();
            if (db.delete(getTableName(), getWhereClause(where), getWhereArgs(where, whereArgs)) > 0) {
                deleted = true;
            }
            db.close();
        } else {
            // Setting is_active to false.
            OValues values = new OValues();
            values.put("is_dirty", "true");
            values.put("is_active", "false");
            if (update(values, where, whereArgs) > 0)
                deleted = true;
        }
        return deleted;
    }

    /**
     * Creates the values.
     * 
     * @param db
     *            the db
     * @param values
     *            the values
     * @return the content values
     */
    private ContentValues createValues(OValues values) {
        ContentValues vals = new ContentValues();
        for (OColumn column : getColumns()) {
            // Checking for functional store column
            if (column.isFunctionalColumn() && column.canFunctionalStore()) {
                if ((column.checkRowId() && values.contains(OColumn.ROW_ID) || !column.checkRowId())) {
                    int contains = 0;
                    // getting depends column from values
                    for (String col : column.getFunctionalStoreDepends())
                        if (values.contains(col))
                            contains++;
                    if (contains == column.getFunctionalStoreDepends().size()) {
                        // Getting functional value before create or update
                        vals.put(column.getName(), getFunctionalMethodValue(column, values).toString());
                    }
                }
            }
            if (values.contains(column.getName())) {
                if (column.getRelationType() == null) {
                    if (values.get(column.getName()) != null) {
                        vals.put(column.getName(), values.get(column.getName()).toString());
                    }
                } else {
                    switch (column.getRelationType()) {
                    case ManyToOne:
                        vals.put(column.getName(), values.get(column.getName()).toString());
                        break;
                    default:
                        break;
                    }
                }
            }
        }
        vals.put("local_write_date", ODate.getDate());
        return vals;
    }

    /**
     * Adds the many to many record.
     * 
     * @param column
     *            the column
     * @param base_id
     *            the base_id
     * @param relation_record_id
     *            the relation_record_id
     */
    public void addManyToManyRecord(String column, Integer base_id, Integer relation_record_id) {
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(relation_record_id);
        addManyToManyRecords(column, base_id, ids);
    }

    /**
     * Adds the many to many records.
     * 
     * @param column
     *            the column
     * @param base_id
     *            the base_id
     * @param relation_record_ids
     *            the relation_record_ids
     */
    public void addManyToManyRecords(String column, Integer base_id, List<Integer> relation_record_ids) {
        handleManyToManyData(column, base_id, relation_record_ids, Command.Add);
    }

    /**
     * Delete many to many record.
     * 
     * @param column
     *            the column
     * @param base_id
     *            the base_id
     * @param relation_record_id
     *            the relation_record_id
     */
    public void deleteManyToManyRecord(String column, Integer base_id, Integer relation_record_id) {
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(relation_record_id);
        deleteManyToManyRecords(column, base_id, ids);
    }

    /**
     * Delete many to many records.
     * 
     * @param column
     *            the column
     * @param base_id
     *            the base_id
     * @param relation_record_ids
     *            the relation_record_ids
     */
    public void deleteManyToManyRecords(String column, Integer base_id, List<Integer> relation_record_ids) {
        handleManyToManyData(column, base_id, relation_record_ids, Command.Delete);
    }

    /**
     * Replace many to many record.
     * 
     * @param column
     *            the column
     * @param base_id
     *            the base_id
     * @param relation_record_id
     *            the relation_record_id
     */
    public void replaceManyToManyRecord(String column, Integer base_id, Integer relation_record_id) {
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(relation_record_id);
        replaceManyToManyRecords(column, base_id, ids);

    }

    /**
     * Replace many to many records.
     * 
     * @param column
     *            the column
     * @param base_id
     *            the base_id
     * @param relation_record_ids
     *            the relation_record_ids
     */
    public void replaceManyToManyRecords(String column, Integer base_id, List<Integer> relation_record_ids) {
        handleManyToManyData(column, base_id, relation_record_ids, Command.Replace);
    }

    /**
     * Handle many to many data.
     * 
     * @param column
     *            the column
     * @param base_id
     *            the base_id
     * @param relation_record_ids
     *            the relation_record_ids
     * @param command
     *            the command
     */
    private void handleManyToManyData(String column, Integer base_id, List<Integer> relation_record_ids,
            Command command) {
        OColumn col = getColumn(column);
        if (col.getRelationType() == RelationType.ManyToMany) {
            SQLiteDatabase db = getWritableDatabase();
            OModel rel_model = createInstance(col.getType());
            manageManyToManyRecords(db, rel_model, relation_record_ids, base_id, command);
            db.close();
        }
    }

    /**
     * Manage many to many records.
     * 
     * @param db
     *            the db
     * @param rel_model
     *            the rel_model
     * @param ids
     *            the ids
     * @param base_id
     *            the base_id
     * @param command
     *            the command
     */
    public void manageManyToManyRecords(SQLiteDatabase db, OModel rel_model, List<Integer> ids, Integer base_id,
            Command command) {
        String table = getTableName() + "_" + rel_model.getTableName() + "_rel";
        String base_column = getTableName() + "_id";
        String rel_column = rel_model.getTableName() + "_id";

        switch (command) {
        case Add:
            // Adding records to relation model
            if (ids.size() > 0) {
                for (int id : ids) {
                    ContentValues values = new ContentValues();
                    values.put(base_column, base_id);
                    values.put(rel_column, id);
                    values.put("odoo_name", mUser.getAndroidName());
                    values.put("local_write_date", ODate.getDate());
                    db.insert(table, null, values);
                }
            }
            break;
        case Update:
            break;
        case Delete:
            // Deleting records to relation model
            if (ids.size() > 0) {
                for (int id : ids) {
                    db.delete(table, base_column + " = ? AND  " + rel_column + " = ?",
                            new String[] { base_id + "", id + "" });
                }
            }
            break;
        case Replace:
            // Removing old entries
            String where = base_column + " = ? ";
            String[] args = new String[] { base_id + "" };
            db.delete(table, getWhereClause(where), getWhereArgs(where, args));
            // Creating new entries
            manageManyToManyRecords(db, rel_model, ids, base_id, Command.Add);
            break;
        }
    }

    /**
     * Gets the where args.
     * 
     * @param where
     *            the where
     * @param whereArgs
     *            the where args
     * @return the where args
     */
    private String[] getWhereArgs(String where, Object[] whereArgs) {
        List<String> args = new ArrayList<String>();
        if (whereArgs != null) {
            for (Object obj : whereArgs) {
                if (obj instanceof int[]) {
                    // List<String> ids = new ArrayList<String>();
                    for (int id : (int[]) obj) {
                        // ids.add(id + "");
                        args.add(id + "");
                    }
                    // args.add(TextUtils.join(",", ids));
                } else if (obj instanceof ArrayList<?>) {
                    for (Object id : (ArrayList<?>) obj) {
                        // ids.add(id + "");
                        args.add(id + "");
                    }
                    // args.add(TextUtils.join("','", ids));
                } else {
                    args.add(obj.toString());
                }
            }
        }
        args.add((mUser != null) ? mUser.getAndroidName() : "");
        if (!mCheckInActiveRecord)
            args.add("true");
        return args.toArray(new String[args.size()]);
    }

    /**
     * Gets the where clause.
     * 
     * @param where
     *            the where
     * @return the where clause
     */
    private String getWhereClause(String where) {
        String newWhereClause = (where != null) ? where + " AND " : "";
        if (!mCheckInActiveRecord)
            newWhereClause += "odoo_name = ? AND is_active = ? ";
        else
            newWhereClause += "odoo_name = ? ";
        return newWhereClause;
    }

    /**
     * Gets the sync helper.
     * 
     * @return the sync helper
     */
    public OSyncHelper getSyncHelper() {
        if (mSyncHelper == null)
            mSyncHelper = new OSyncHelper(mContext, mUser, this);
        mSyncingData = true;
        return mSyncHelper;
    }

    /**
     * Sets the syncing data flag.
     * 
     * @param syncingData
     *            the syncing data
     * @return the o model
     */
    public OModel setSyncingDataFlag(Boolean syncingData) {
        mSyncingData = syncingData;
        return this;
    }

    /**
     * Gets the model values.
     * 
     * @param model
     *            the model
     * @return the model values
     */
    public List<OValues> getModelValues(String model) {
        List<String> models = new ArrayList<String>();
        models.add(model);
        return getModelValues(models);

    }

    /**
     * Gets the model values.
     * 
     * @param models
     *            the models
     * @return the model values
     */
    public List<OValues> getModelValues(List<String> models) {
        if (mSyncHelper == null)
            mSyncHelper = new OSyncHelper(mContext, mUser, this);
        return mSyncHelper.modelInfo(models);
    }

    /**
     * Gets the.
     * 
     * @param context
     *            the context
     * @param model_name
     *            the model_name
     * @return the o model
     */
    public static OModel get(Context context, String model_name) {
        OModel model = null;
        try {
            PreferenceManager pfManager = new PreferenceManager(context);
            Class<?> model_class = Class.forName(pfManager.getString(model_name, null));
            if (model_class != null)
                model = new OModel(context, model_name).createInstance(model_class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return model;
    }

    /**
     * Checks if is empty table.
     * 
     * @return true, if is empty table
     */
    public boolean isEmptyTable() {
        if (count() <= 0)
            return true;
        return false;
    }

    /**
     * Ids.
     * 
     * @return the list
     */
    public List<Integer> ids() {
        List<Integer> ids = new ArrayList<Integer>();
        for (ODataRow row : select()) {
            ids.add(row.getInt("id"));
        }
        return ids;
    }

    /**
     * Before create row.
     * 
     * @param column
     *            the column
     * @param original_record
     *            the original_record
     * @return the JSON object
     */
    public JSONObject beforeCreateRow(OColumn column, JSONObject original_record) {
        return original_record;
    }

    /**
     * Check in active record.
     * 
     * @param checkInactiveRecord
     *            the check inactive record
     */
    public void checkInActiveRecord(Boolean checkInactiveRecord) {
        mCheckInActiveRecord = checkInactiveRecord;
    }

    /**
     * Check for local update.
     * 
     * @return the boolean
     */
    public Boolean checkForLocalUpdate() {
        return true;
    }

    /**
     * Can update to server.
     * 
     * @return the boolean
     */
    public Boolean canUpdateToServer() {
        return true;
    }

    /**
     * Can delete from server.
     * 
     * @return the boolean
     */
    public Boolean canDeleteFromServer() {
        return true;
    }

    /**
     * Can delete from local.
     * 
     * @return the boolean
     */
    public Boolean canDeleteFromLocal() {
        return true;
    }

    /**
     * Can create on server.
     * 
     * @return the boolean
     */
    public Boolean canCreateOnServer() {
        return true;
    }

    /**
     * Check for write date.
     * 
     * @return the boolean
     */
    public Boolean checkForWriteDate() {
        return true;
    }

    /**
     * Check for create date.
     * 
     * @return the boolean
     */
    public Boolean checkForCreateDate() {
        return true;
    }

    public Boolean checkForLocalLatestUpdate() {
        return true;
    }

    public OUser user() {
        return mUser;
    }

    public OModel setLimit(Integer limit) {
        mLimit = limit;
        return this;
    }

    public OModel setOffset(Integer offset_index) {
        mOffset = offset_index;
        return this;
    }

    public Integer getNextOffset() {
        return mOffset + mLimit;
    }

    public void setCreateWriteLocal(Boolean make_local) {
        if (make_local) {
            write_date.setLocalColumn();
            create_date.setLocalColumn();
        }
    }

    /**
     * browse, New API
     * 
     * @return OQuery object
     */
    public OQuery browse() {
        return new OQuery(mContext, this, QueryType.Select);
    }

    /**
     * The Class AutoUpdateOnServer.
     */
    class AutoUpdateOnServer extends AsyncTask<Void, Void, Void> {

        /*
         * (non-Javadoc)
         * 
         * @see android.os.AsyncTask#doInBackground(Params[])
         */
        @Override
        protected Void doInBackground(Void... params) {
            getSyncHelper().syncWithServer();
            return null;
        }
    }

    public OContentResolver resolver() {
        return new OContentResolver(this, mContext);
    }

}

/**
 * The Interface OModelHelper.
 */
interface OModelHelper {

    /**
     * Gets the columns.
     * 
     * @return the columns
     */
    public List<OColumn> getColumns();

    /**
     * Gets the columns.
     * 
     * @param local
     *            the local
     * @return the columns
     */
    public List<OColumn> getColumns(Boolean local);

    /**
     * Default domain.
     * 
     * @return the o domain
     */
    public ODomain defaultDomain();
}