/* Copyright (C) 2004 - 2008  db4objects Inc.  http://www.db4o.com

This file is part of the db4o open source object database.

db4o is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation and as clarified by db4objects' GPL 
interpretation policy, available at
http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
Suite 350, San Mateo, CA 94403, USA.

db4o 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 General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. */
package com.db4o.reflect.core;

import java.lang.reflect.*;

import com.db4o.reflect.*;

/**
 * @exclude
 */
public abstract class AbstractReflectArray implements ReflectArray {
	
	protected final Reflector _reflector;

	public AbstractReflectArray(Reflector reflector) {
		_reflector = reflector;
	}

	public abstract Object newInstance(ReflectClass componentType, int[] dimensions);

	public abstract Object newInstance(ReflectClass componentType, int length);

	public int[] dimensions(Object arr) {
	    int count = 0;
	    ReflectClass claxx = _reflector.forObject(arr);
	    while (claxx.isArray()) {
	        count++;
	        claxx = claxx.getComponentType();
	    }
	    int dim[] = new int[count];
	    for (int i = 0; i < count; i++) {
	        try {
	            dim[i] = getLength(arr);
	            arr = get(arr, 0);
	        } catch (Exception e) {
	            return dim;
	        }
	    }
	    return dim;
	}

	public int flatten(Object a_shaped, int[] a_dimensions, int a_currentDimension, Object[] a_flat,
			int a_flatElement) {
			    if (a_currentDimension == (a_dimensions.length - 1)) {
			        for (int i = 0; i < a_dimensions[a_currentDimension]; i++) {
			            a_flat[a_flatElement++] = getNoExceptions(a_shaped, i);
			        }
			    } else {
			        for (int i = 0; i < a_dimensions[a_currentDimension]; i++) {
			            a_flatElement =
			                flatten(
			                    getNoExceptions(a_shaped, i),
			                    a_dimensions,
			                    a_currentDimension + 1,
			                    a_flat,
			                    a_flatElement);
			        }
			    }
			    return a_flatElement;
			}

	public Object get(Object onArray, int index) {
	    return Array.get(onArray, index);
	}

	public ReflectClass getComponentType(ReflectClass a_class) {
	    while (a_class.isArray()) {
	        a_class = a_class.getComponentType();
	    }
	    return a_class;
	}

	public int getLength(Object array) {
	    return Array.getLength(array);
	}

	private final Object getNoExceptions(Object onArray, int index) {
	    try {
	        return get(onArray, index);
	    } catch (Exception e) {
	        return null;
	    }
	}

	public boolean isNDimensional(ReflectClass a_class) {
	    return a_class.getComponentType().isArray();
	}

	public void set(Object onArray, int index, Object element) {
	    if(element == null){
	        try{
	            Array.set(onArray, index, element);
	        }catch(Exception e){
	            // This can happen on primitive arrays
	            // and we are fine with ignoring it.
	        	// TODO: check if it's a primitive array first and don't ignore exceptions
	        }
	        
	    }else{
	        Array.set(onArray, index, element);
	    }
	}

	public int shape(Object[] a_flat, int a_flatElement, Object a_shaped, int[] a_dimensions,
			int a_currentDimension) {
			    if (a_currentDimension == (a_dimensions.length - 1)) {
			        for (int i = 0; i < a_dimensions[a_currentDimension]; i++) {
			            set(a_shaped, i, a_flat[a_flatElement++]);
			        }
			    } else {
			        for (int i = 0; i < a_dimensions[a_currentDimension]; i++) {
			            a_flatElement =
			                shape(
			                    a_flat,
			                    a_flatElement,
			                    get(a_shaped, i),
			                    a_dimensions,
			                    a_currentDimension + 1);
			        }
			    }
			    return a_flatElement;
			}

}
