/* XSSFPivotTable.java

{{IS_NOTE
	Purpose:
		
	Description:
		
	History:
		Apr 25, 2012 6:44:57 PM , Created by Sam
}}IS_NOTE

Copyright (C) 2012 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
}}IS_RIGHT
*/
package org.zkoss.poi.xssf.usermodel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;

import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColFields;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColItems;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataField;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataFields;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTField;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTI;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTLocation;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPivotField;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPivotFields;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPivotTableDefinition;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRowFields;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRowItems;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTX;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.PivotTableDefinitionDocument;
import org.zkoss.poi.POIXMLDocumentPart;
import org.zkoss.poi.openxml4j.opc.PackagePart;
import org.zkoss.poi.openxml4j.opc.PackageRelationship;
import org.zkoss.poi.openxml4j.opc.TargetMode;
import org.zkoss.poi.openxml4j.opc.internal.MemoryPackagePart;
import org.zkoss.poi.ss.usermodel.Calculation;
import org.zkoss.poi.ss.usermodel.DataField;
import org.zkoss.poi.ss.usermodel.PivotCache;
import org.zkoss.poi.ss.usermodel.PivotCache.CacheField;
import org.zkoss.poi.ss.usermodel.PivotField;
import org.zkoss.poi.ss.usermodel.PivotField.Item;
import org.zkoss.poi.ss.usermodel.PivotField.Type;
import org.zkoss.poi.ss.usermodel.PivotTable;
import org.zkoss.poi.ss.usermodel.Sheet;
import org.zkoss.poi.ss.util.AreaReference;
import org.zkoss.poi.ss.util.CellReference;
import org.zkoss.poi.ss.util.ItemInfo;
import org.zkoss.poi.xssf.usermodel.XSSFPivotField.XSSFItem;

public class XSSFPivotTable extends POIXMLDocumentPart implements PivotTable, Serializable {
	private static final long serialVersionUID = -8138808664919441702L;

	private CTPivotTableDefinition _pivotTableDefinition;
	
	private XSSFPivotCache _pivotCache;
	
	/*PivotField's name to PivotField*/
	private LinkedHashMap<String, PivotField> _pivotFields;
	
	/*package*/ XSSFPivotTable() {
		super();
		onDocumentCreate();
	}
	
	protected void onDocumentCreate() {
		_pivotTableDefinition = CTPivotTableDefinition.Factory.newInstance();
		
		//default value
		_pivotTableDefinition.setApplyNumberFormats(false);
		_pivotTableDefinition.setApplyBorderFormats(false);
		_pivotTableDefinition.setApplyFontFormats(false);
		_pivotTableDefinition.setApplyPatternFormats(false);
		_pivotTableDefinition.setApplyAlignmentFormats(false);
		_pivotTableDefinition.setApplyWidthHeightFormats(true);
		_pivotTableDefinition.setDataCaption("Values");
		_pivotTableDefinition.setShowCalcMbrs(false);
		_pivotTableDefinition.setUseAutoFormatting(true);
		_pivotTableDefinition.setItemPrintTitles(true);
		_pivotTableDefinition.setIndent(0);
		_pivotTableDefinition.setOutlineData(true);
		_pivotTableDefinition.setMultipleFieldFilters(false);
	}
	
	public XSSFPivotTable(PackagePart part, PackageRelationship rel, List<PivotCache> pivotCaches) throws IOException, XmlException {
		final InputStream in = part.getInputStream();
		_pivotTableDefinition = PivotTableDefinitionDocument.Factory.parse(in).getPivotTableDefinition();
		
		long cacheId = getCacheId();
		for (PivotCache e : pivotCaches) {
			if (e.getCacheId() == cacheId) {
				_pivotCache = (XSSFPivotCache)e;
				break;
			}
		}
	}
	
	protected void commit() throws IOException {
		XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
		xmlOptions.setSaveSyntheticDocumentElement(new QName(CTPivotTableDefinition.type.getName().getNamespaceURI(), "pivotTableDefinition"));
        Map<String, String> map = new HashMap<String, String>();
        map.put(STRelationshipId.type.getName().getNamespaceURI(), "r");
        xmlOptions.setSaveSuggestedPrefixes(map);
		
        PackagePart part = getPackagePart();
        clearMemoryPackagePart(part);
        
        OutputStream out = part.getOutputStream();
        _pivotTableDefinition.save(out, xmlOptions);
        out.close();
	}

	public CTPivotTableDefinition getPivotTableDefinition() {
		return _pivotTableDefinition;
	}

	private String nextReleationId() {
		int idx = 1;
		String rId = "rId" + idx;
		while (getRelationById(rId) != null) {
			rId = "rId" + (++idx);
		}
		return rId;
	}
	
	public void setPivotCache(PivotCache pivotCache) {
		_pivotCache = (XSSFPivotCache)pivotCache;
		
		getPackagePart().addRelationship(_pivotCache.getPackagePart().getPartName(), TargetMode.INTERNAL, XSSFRelation.PIVOT_CACHE_DEFINITION.getRelation());
		addRelation(nextReleationId(), _pivotCache);
		
		_pivotTableDefinition.setCacheId(pivotCache.getCacheId());

		_pivotTableDefinition.setUpdatedVersion(pivotCache.getRefreshedVersion());
		_pivotTableDefinition.setMinRefreshableVersion(pivotCache.getMinRefreshableVersion());
		_pivotTableDefinition.setCreatedVersion(pivotCache.getCreatedVersion());
	}

	
	
	public long getCacheId() {
		return _pivotTableDefinition.getCacheId();
	}

	
	public List<PivotField> getColumnFields() {
		CTColFields colFields = _pivotTableDefinition.getColFields();
		if (colFields == null) {
			return Collections.emptyList();
		}
		
		List<PivotField> list = new ArrayList<PivotField>();
		List<PivotField> pivotFields = getPivotFields();
		for (CTField f : colFields.getFieldList()) {
			int x = f.getX();
			if (x >= 0) {
				list.add(pivotFields.get(x));
			}
		}
		return list;
	}
	
	private static CTColFields initColFields(CTPivotTableDefinition pivotTableDefinition) {
		CTColFields ctColFields = pivotTableDefinition.addNewColFields();
		CTRowItems rowItems = pivotTableDefinition.getRowItems();
		if (rowItems == null) {
			rowItems = pivotTableDefinition.addNewRowItems();
			rowItems.addNewI();
			rowItems.setCount(1);
		}
		return ctColFields;
	}
	
	
	public void setColumnField(PivotField field) {
		int idx = getPivotFieldIndex(field, getPivotFields());
		if (idx < 0) {
			throw new IllegalArgumentException("Can't find PivotField");
		}
		
		boolean typeChanged = false;
		Type type = field.getType();
		if (type != null) {
			switch (type) {
			case ROW:
				typeChanged = true;
				removeRowField(idx, field);
				break;
			case DATA:
				typeChanged = true;
				removeDataField(idx, field);
				break;
			}	
		} else {
			typeChanged = true;
		}
		
		if (typeChanged) {
			//1. PivotField
			field.setType(PivotField.Type.COLUMN);
			
			//2. colFields
			CTColFields colFields = _pivotTableDefinition.getColFields();
			if (colFields == null) {
				colFields = initColFields(_pivotTableDefinition);
			}
			
			int count = (int)colFields.getCount();
			List<DataField> dataFields = getDataFields();
			boolean insrtDataCol = false;
			if (dataFields.size() > 1 && !getDataOnRows()) {
				insrtDataCol = true;
				
				int lastIdx = count - 1;
				if (lastIdx >= 0) {
					CTField f = colFields.getFieldList().get(lastIdx);
					if (f.getX() == -2) {//the date field index use -2
						colFields.removeField(lastIdx);
					}	
				}
			}

			colFields.addNewField().setX(idx);
			
			if (insrtDataCol) {
				colFields.addNewField().setX(-2);
			}
			colFields.setCount(colFields.getFieldList().size());
		}
	}
	
	public void setDataCaption(String caption) {
		_pivotTableDefinition.setDataCaption(caption);
	}

	
	public String getDataCaption() {
		return _pivotTableDefinition.getDataCaption();
	}

	
	public List<DataField> getDataFields() {
		CTDataFields dataFields = _pivotTableDefinition.getDataFields();
		if (dataFields == null) {
			return Collections.emptyList();
		}
		
		ArrayList<DataField> list = new ArrayList<DataField>();
		List<PivotField> pivotFields = getPivotFields();
		List<CTDataField> dataFieldList = dataFields.getDataFieldList();
		for (CTDataField dataField : dataFieldList) {
			PivotField pivotField = pivotFields.get((int)dataField.getFld());
			list.add(new XSSFDataField(dataField, pivotField));
		}
		return list;
	}
	
	
	public void setDataField(PivotField field, String name, Calculation subtotal) {
		
		CTDataFields dataFields = _pivotTableDefinition.getDataFields();
		if (dataFields == null) {
			dataFields = _pivotTableDefinition.addNewDataFields();
		}
		CTDataField dataField = dataFields.addNewDataField();
		dataField.setName(name);
		int idx = getPivotFieldIndex(field, getPivotFields());
		if (idx < 0) {
			throw new IllegalArgumentException("Can't find PivotField");
		}
		field.setType(Type.DATA);
		dataField.setFld(idx);
		dataField.setSubtotal(XSSFDataField.getSubtotalType(subtotal));
	
		//TODO: default ?
		dataField.setBaseField(0);
		dataField.setBaseItem(0);
		dataFields.setCount(dataFields.getDataFieldList().size());
		
		CTRowItems rowItems = _pivotTableDefinition.getRowItems();
		if (rowItems == null) {
			rowItems = _pivotTableDefinition.addNewRowItems();
			rowItems.addNewI();
			rowItems.setCount(rowItems.getIList().size());
		}
		CTColItems colItems = _pivotTableDefinition.getColItems();
		if (colItems == null) {
			colItems = _pivotTableDefinition.addNewColItems();
			colItems.addNewI();
			colItems.setCount(colItems.getIList().size());
		}
	}
	
	public void setFirstHeaderRow(int row) {
		getCTLocation().setFirstHeaderRow(row);
	}
	
	public CTLocation getCTLocation() {
		CTLocation location = _pivotTableDefinition.getLocation();
		if (location == null) {
			return _pivotTableDefinition.addNewLocation();
		}
		return location;
	}
	

	public CTColFields getCTColFields() {
		CTColFields colFields = _pivotTableDefinition.getColFields();
		if (colFields == null) {
			return initColFields(_pivotTableDefinition);
		}
		return colFields;
	}
	

	public CTRowFields getCTRowFields() {
		CTRowFields rowFields = _pivotTableDefinition.getRowFields();
		if (rowFields == null) {
			return initRowFields(_pivotTableDefinition);
		}
		return rowFields;
	}
	
	public void setFirstData(int row, int col) {
		CTLocation ctLocation = getCTLocation();
		ctLocation.setFirstDataRow(row);
		ctLocation.setFirstDataCol(col);
	}
	
	public void setLocationRef(AreaReference ref) {
		getCTLocation().setRef(ref.formatAsString());
	}
	
	public AreaReference getLocationRef() {
		return new AreaReference(_pivotTableDefinition.getLocation().getRef());
	}
	
	public CellReference getFirstDataRef() {
		CellReference firstCellRef = getLocationRef().getFirstCell();
		
		CTLocation l = getCTLocation();
		int col = firstCellRef.getCol() + (int) l.getFirstDataCol();
		int row = firstCellRef.getRow() + (int) l.getFirstDataRow();
		
		return new CellReference(row, col);
	}

	
	public void setGrandTotalCaption(String caption) {
		_pivotTableDefinition.setGrandTotalCaption(caption);
	}
	
	
	public String getGrandTotalCaption() {
		return _pivotTableDefinition.getGrandTotalCaption();
	}

	
	public void setName(String name) {
		_pivotTableDefinition.setName(name);
	}
	
	
	public String getName() {
		return _pivotTableDefinition.getName();
	}

	
	public PivotCache getPivotCache() {
		return _pivotCache;
	}
	
	
	public PivotField getPivotField(String name) {
		List<PivotField> pivotFields = getPivotFields();
		for (PivotField f : pivotFields) {
			String fName = f.getName();
			if (fName.equalsIgnoreCase(name)) {
				return f;
			}
		}
		return null;
	}

	
	public List<PivotField> getPivotFields() {
		if (_pivotFields == null) {
			initPivotFields();
		}
		return new ArrayList<PivotField>(_pivotFields.values());
	}
	
	private void initPivotFields() {
		_pivotFields = new LinkedHashMap<String, PivotField>();
		CTPivotFields pivotFields = _pivotTableDefinition.getPivotFields();
		if (pivotFields == null)
			return;
		
		Iterator<CacheField> iterator = _pivotCache.getFields().iterator();
		for (CTPivotField f : pivotFields.getPivotFieldList()) {
			XSSFPivotField pf = new XSSFPivotField(f, iterator.next(), this);
			_pivotFields.put(pf.getName(), pf);
		}
	}

	private int getPivotFieldIndex(PivotField pivotField, List<PivotField> from) {
		int i = 0;
		for (PivotField pf : from) {
			if (pf.equals(pivotField)) {
				return i; 
			}
			i++;
		}
		return -1;
	}
	
	
	public List<PivotField> getRowFields() {
		CTRowFields rowFields = _pivotTableDefinition.getRowFields();
		if (rowFields == null) {
			return Collections.emptyList();
		}
		
		List<PivotField> list = new ArrayList<PivotField>();
		List<PivotField> pivotFields = getPivotFields();
		for (CTField f : rowFields.getFieldList()) {
			int idx = f.getX();
			if (idx >= 0) {
				list.add(pivotFields.get(idx));
			}
		}
		return list;
	}
	
	private static CTRowFields initRowFields(CTPivotTableDefinition pivotTableDefinition) {
		CTRowFields rowFields = pivotTableDefinition.addNewRowFields();
		CTColItems colItems = pivotTableDefinition.getColItems();
		if (colItems == null) {
			colItems = pivotTableDefinition.addNewColItems();
			colItems.addNewI();
			colItems.setCount(1);
		}
		return rowFields;
	}
	
	public Sheet getSheet() {
		return (Sheet)getParent();
	}
	
	private ItemInfo getNonDataTypeItem(List<ItemInfo> items) {
		for (int i = 0; i < items.size(); i++) {
			ItemInfo info = items.get(i);
			if (info != null && info.getType() != Item.Type.DATA) {
				return info;
			}
		}
		return null;
	}
	
	public void setColumnItems(List<List<ItemInfo>> items) {
		CTColItems colItems = _pivotTableDefinition.getColItems();
		if (colItems != null) {
			_pivotTableDefinition.unsetColItems();
		}
		colItems = _pivotTableDefinition.addNewColItems();
		
		IndexMapper indexMapper = new IndexMapper(getColumnFields());
		DataFieldIndexMapper dataIndexMapper = null;
		List<DataField> dataFields = getDataFields();
		if (!getDataOnRows() && dataFields.size() > 1) {
			dataIndexMapper = new DataFieldIndexMapper(dataFields);
		}
		
		int itemSize = items.size();
		for (int c = 0; c < itemSize; c++) {
			
			List<ItemInfo> col = items.get(c);
			ItemInfo info = getNonDataTypeItem(col);
			if (info != null) {
				CTI ctI = colItems.addNewI();
				if (info.getDepth() > 0) {
					ctI.setR(info.getDepth());
				}
				
				Item.Type type = info.getType();
				CTX ctX = ctI.addNewX();
				ctI.setT(XSSFItem.getCTItemType(type));
				//ctI.setI(index);
				
				if (type == Item.Type.GRAND) {
					int index = info.getIndex();
					if (index > 0) {
						ctX.setV(index);
					}	
				} else {
					int index = indexMapper.getShareItemIndex(info.getDepth(), info.getValue());
					if (index > 0) {
						ctX.setV(index);
					}
				}
			} else {
				
				boolean setRowIndex = false;
				Integer firstRowIndex = null;
				CTI ctI = colItems.addNewI();
				for (int i = 0; i < col.size(); i++) {
					ItemInfo item = col.get(i);
					if (item != null) {
						if (firstRowIndex == null) {
							firstRowIndex = i;
						}
						int depth = item.getDepth();
						if (depth >= 0) {
							CTX ctX = ctI.addNewX();
							int idx = indexMapper.getShareItemIndex(i, item.getValue());
							if (idx < 0) {
								throw new IllegalArgumentException("can't find item: " + item.getValue());
							} else if (idx > 0) {
								ctX.setV(idx);
							}	
						} else { //column data field
							int idx = dataIndexMapper.getIndex((String)item.getValue());
							if (idx < 0) {
								throw new IllegalArgumentException("can't find item: " + item.getValue());
							}
							
							CTX ctX = ctI.addNewX();
							if (idx > 0) {
								ctI.setI(idx);
								ctX.setV(idx);
							}
						}
					} else {
						setRowIndex = true;
					}
				}
				if (setRowIndex && firstRowIndex != null) {
					ctI.setR(firstRowIndex);
				}
			}
		}
		colItems.setCount(colItems.getIList().size());
	}
	
	
	public void setRowItems(List<List<ItemInfo>> items) {
		CTRowItems rowItems = _pivotTableDefinition.getRowItems();
		if (rowItems != null) {//clear old row items
			_pivotTableDefinition.unsetRowItems();
		}
		rowItems = _pivotTableDefinition.addNewRowItems();
		
		IndexMapper indexMapper = new IndexMapper(getRowFields());
		DataFieldIndexMapper dataIndexMapper = null;
		List<DataField> dataFields = getDataFields();
		if (getDataOnRows() && dataFields.size() > 1) {
			dataIndexMapper = new DataFieldIndexMapper(dataFields);
		}
		
		int itemSize = items.size();
		for (int i = 0; i < itemSize; i++) {
			List<ItemInfo> row = items.get(i);
			ItemInfo info = getNonDataTypeItem(row);
			if (info != null) {
				CTI ctI = rowItems.addNewI();
				if (info.getDepth() > 0) {
					ctI.setR(info.getDepth());
				}
				
				Item.Type type = info.getType();
				CTX ctX = ctI.addNewX();
				ctI.setT(XSSFItem.getCTItemType(type));
				//ctI.setI(index);
				
				if (type == Item.Type.GRAND) {
					int index = info.getIndex();
					if (index > 0) {
						ctX.setV(index);
					}	
				} else {
					int index = indexMapper.getShareItemIndex(info.getDepth(), info.getValue());
					if (index > 0) {
						ctX.setV(index);
					}
				}
			} else {
				boolean setColumnIndex = false;
				Integer firstColumnIndex = null;
				CTI ctI = rowItems.addNewI();
				
				for (int c = 0; c < row.size(); c++) {
					ItemInfo item = row.get(c);
					if (item != null) {
						if (firstColumnIndex == null) {
							firstColumnIndex = c;
						}
						
						int depth = item.getDepth();
						if (depth >= 0) {
							CTX ctX = ctI.addNewX();
							int idx = indexMapper.getShareItemIndex(c, item.getValue());
							if (idx < 0) {
								throw new IllegalArgumentException("can't find item: " + item.getValue());
							} else if (idx > 0) {
								ctX.setV(idx);
							}
						} else {//row data field
							int idx = dataIndexMapper.getIndex((String)item.getValue());
							if (idx < 0) {
								throw new IllegalArgumentException("can't find item: " + item.getValue());
							}
							
							CTX ctX = ctI.addNewX();
							if (idx > 0) {
								ctI.setI(idx);
								ctX.setV(idx);
							}
						}
					} else {
						setColumnIndex = true;
					}
				}
				if (setColumnIndex && firstColumnIndex != null) {
					ctI.setR(firstColumnIndex);
				}
			}
		}
		rowItems.setCount(rowItems.getIList().size());
	}
	
	public void setRowField(PivotField field) {
		int idx = getPivotFieldIndex(field, getPivotFields());
		if (idx < 0) {
			throw new IllegalArgumentException("Can't find PivotField");
		}
		
		boolean typeChanged = false;
		Type type = field.getType();
		if (type != null) {
			switch (type) {
			case COLUMN:
				typeChanged = true;
				removeColumnField(idx, field);
				break;
			case DATA:
				typeChanged = true;
				removeDataField(idx, field);
				break;
			}	
		} else {
			typeChanged = true;
		}
		
		if (typeChanged) {
			//1. pivotField
			field.setType(PivotField.Type.ROW);
			
			//2. rowFields 
			CTRowFields rowFields = _pivotTableDefinition.getRowFields();
			if (rowFields == null) {
				rowFields = initRowFields(_pivotTableDefinition);
			}
			
			int count = (int)rowFields.getCount();
			List<DataField> dataFields = getDataFields();
			boolean insrtDataRow = false;
			if (dataFields.size() > 1 && getDataOnRows()) {
				insrtDataRow = true;
				
				int lastIdx = count - 1;
				if (lastIdx >= 0) {
					CTField f = rowFields.getFieldList().get(lastIdx);
					if (f.getX() == -2) {//the date row field index use -2
						rowFields.removeField(lastIdx);
					}
				}
			}
			
			rowFields.addNewField().setX(idx);
			
			if (insrtDataRow) {
				rowFields.addNewField().setX(-2);
			}
			rowFields.setCount(rowFields.getFieldList().size());
		}
	}

	private void removeDataField(int idx, PivotField field) {
		CTDataField dataField = null;
		CTDataFields dataFields = _pivotTableDefinition.getDataFields();
		
		int i = 0;
		for (CTDataField d : dataFields.getDataFieldList()) {
			if (d.getFld() == idx) {
				dataField = d;
				break;
			}
			i++;
		}
		
		if (dataField != null) {
			dataFields.removeDataField(i);
			dataFields.setCount(dataFields.getDataFieldList().size());
		}
	}
	
	private void removeRowField(int idx, PivotField field) {
		CTField ctField = null;
		CTRowFields rowFields = _pivotTableDefinition.getRowFields();
		
		int i = 0;
		for (CTField f : rowFields.getFieldList()) {
			if (f.getX() == idx) {
				ctField = f;
				break;
			}
		}
		
		if (ctField != null) {
			rowFields.removeField(i);
			rowFields.setCount(rowFields.getFieldList().size());
		}
	}

	private void removeColumnField(int idx, PivotField field) {
		CTField ctField = null;
		CTColFields colFields = _pivotTableDefinition.getColFields();
		
		int i = 0;
		for (CTField f : colFields.getFieldList()) {
			if (f.getX() == idx) {
				ctField = f;
				break;
			}
			i++;
		}
		
		if (ctField != null) {
			colFields.removeField(i);
			colFields.setCount(colFields.getFieldList().size());	
		}
	}

	@Override
	public void setDataOnRows(boolean dataOnRows) {
		_pivotTableDefinition.setDataOnRows(dataOnRows);
	}

	@Override
	public boolean getDataOnRows() {
		return _pivotTableDefinition.getDataOnRows();
	}
	
	@Override
	public String getRowHeaderCaption() {
		return _pivotTableDefinition.getRowHeaderCaption();
	}
	
	@Override
	public void setRowHeaderCaption(String caption) {
		_pivotTableDefinition.setRowHeaderCaption(caption);
	}
	
	@Override
	public void setOutline(boolean outline) {
		_pivotTableDefinition.setOutline(outline);
	}

	@Override
	public boolean getOutline() {
		return _pivotTableDefinition.getOutline();
	}

	@Override
	public void setOutlineData(boolean outlineData) {
		_pivotTableDefinition.setOutlineData(outlineData);
	}

	@Override
	public boolean getOutlineData() {
		return _pivotTableDefinition.getOutlineData();
	}
	
	private class IndexMapper {
		List<HashMap<Object, Integer>> _mapper = new ArrayList<HashMap<Object, Integer>>();
		
		IndexMapper(List<PivotField> fields) {
			
			for (PivotField pivotField : fields) {
				HashMap<Object, Integer> map = new HashMap<Object, Integer>();
				_mapper.add(map);
				List<Item> items = pivotField.getItems();
				for (int j = 0; j < items.size(); j++) {
					Item item = items.get(j);
					Object value = item.getValue();
					if (value instanceof Calendar) {
						map.put(((Calendar)value).getTime(), j);
					} else if (value instanceof Number) {
						map.put(((Number)value).doubleValue(), j);
					} else {
						map.put(value != null ? value.toString() : null, j);
					}	
				}
			}
		}
		
		int getShareItemIndex(int fieldIndex, Object key) {
			HashMap<Object, Integer> map = _mapper.get(fieldIndex);
			
			Integer index = map.get(key);
			if (index != null) {
				return index;
			}
			
			return -1;
		} 
	}
	
	private class DataFieldIndexMapper {
		HashMap<String, Integer> _mapper = new HashMap<String, Integer>();
		DataFieldIndexMapper(List<DataField> dataFields) {
			for (int i = 0; i < dataFields.size(); i++) {
				DataField f = dataFields.get(i);
				_mapper.put(f.getName(), Integer.valueOf(i));
			}
		}
		
		int getIndex(String str) {
			Integer index = _mapper.get(str);
			if (index != null) {
				return index;
			}
			return -1;
		}
	}

}