/* ====================================================================
Licensed to the Apache Software Foundation (ASF) Under one or more
contributor license agreements. See the NOTICE file distributed with
this work for Additional information regarding copyright ownership.
The ASF licenses this file to You Under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed Under the License is distributed on an "AS Is" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations Under the License.
==================================================================== */
namespace HH.WMS.Utils.NPOI.HSSF.Record.Aggregates
{
using System;
using System.Text;
using System.Collections;
using HH.WMS.Utils.NPOI.HSSF.Record;
using HH.WMS.Utils.NPOI.HSSF.Util;
using HH.WMS.Utils.NPOI.HSSF.Model;
using HH.WMS.Utils.NPOI.Util;
using HH.WMS.Utils.NPOI.SS.Formula;
using System.Collections.Generic;
using HH.WMS.Utils.NPOI.SS.Util;
using HH.WMS.Utils.NPOI.SS.Formula.PTG;
///
///
///
/// CFRecordsAggregate - aggregates Conditional Formatting records CFHeaderRecord
/// and number of up to three CFRuleRecord records toGether to simplify
/// access to them.
/// @author Dmitriy Kumshayev
public class CFRecordsAggregate : RecordAggregate
{
/** Excel allows up to 3 conditional formating rules */
private const int MAX_CONDTIONAL_FORMAT_RULES = 3;
public const short sid = -2008; // not a real BIFF record
//private static POILogger log = POILogFactory.GetLogger(typeof(CFRecordsAggregate));
private CFHeaderRecord header;
/** List of CFRuleRecord objects */
private List rules;
private CFRecordsAggregate(CFHeaderRecord pHeader, CFRuleRecord[] pRules)
{
if (pHeader == null)
{
throw new ArgumentException("header must not be null");
}
if (pRules == null)
{
throw new ArgumentException("rules must not be null");
}
if (pRules.Length > MAX_CONDTIONAL_FORMAT_RULES)
{
throw new ArgumentException("No more than "
+ MAX_CONDTIONAL_FORMAT_RULES + " rules may be specified");
}
header = pHeader;
rules = new List(3);
for (int i = 0; i < pRules.Length; i++)
{
rules.Add(pRules[i]);
}
}
public CFRecordsAggregate(CellRangeAddress[] regions, CFRuleRecord[] rules)
: this(new CFHeaderRecord(regions, rules.Length), rules)
{
}
///
/// Create CFRecordsAggregate from a list of CF Records
///
/// list of Record objects
public static CFRecordsAggregate CreateCFAggregate(RecordStream rs)
{
Record rec = rs.GetNext();
if (rec.Sid != CFHeaderRecord.sid)
{
throw new InvalidOperationException("next record sid was " + rec.Sid
+ " instead of " + CFHeaderRecord.sid + " as expected");
}
CFHeaderRecord header = (CFHeaderRecord)rec;
int nRules = header.NumberOfConditionalFormats;
CFRuleRecord[] rules = new CFRuleRecord[nRules];
for (int i = 0; i < rules.Length; i++)
{
rules[i] = (CFRuleRecord)rs.GetNext();
}
return new CFRecordsAggregate(header, rules);
}
///
/// Create CFRecordsAggregate from a list of CF Records
///
/// list of Record objects
/// position of CFHeaderRecord object in the list of Record objects
public static CFRecordsAggregate CreateCFAggregate(IList recs, int pOffset)
{
Record rec = (Record)recs[pOffset];
if (rec.Sid != CFHeaderRecord.sid)
{
throw new InvalidOperationException("next record sid was " + rec.Sid
+ " instead of " + CFHeaderRecord.sid + " as expected");
}
CFHeaderRecord header = (CFHeaderRecord)rec;
int nRules = header.NumberOfConditionalFormats;
CFRuleRecord[] rules = new CFRuleRecord[nRules];
int offset = pOffset;
int countFound = 0;
while (countFound < rules.Length)
{
offset++;
if (offset >= recs.Count)
{
break;
}
rec = (Record)recs[offset];
if (rec is CFRuleRecord)
{
rules[countFound] = (CFRuleRecord)rec;
countFound++;
}
else
{
break;
}
}
if (countFound < nRules)
{ // TODO -(MAR-2008) can this ever happen? Write junit
//if (log.Check(POILogger.DEBUG))
//{
// log.Log(POILogger.DEBUG, "Expected " + nRules + " Conditional Formats, "
// + "but found " + countFound + " rules");
//}
header.NumberOfConditionalFormats = (nRules);
CFRuleRecord[] lessRules = new CFRuleRecord[countFound];
Array.Copy(rules, 0, lessRules, 0, countFound);
rules = lessRules;
}
return new CFRecordsAggregate(header, rules);
}
public override void VisitContainedRecords(RecordVisitor rv)
{
rv.VisitRecord(header);
for (int i = 0; i < rules.Count; i++)
{
CFRuleRecord rule = (CFRuleRecord)rules[i];
rv.VisitRecord(rule);
}
}
///
/// Create a deep Clone of the record
///
public CFRecordsAggregate CloneCFAggregate()
{
CFRuleRecord[] newRecs = new CFRuleRecord[rules.Count];
for (int i = 0; i < newRecs.Length; i++)
{
newRecs[i] = (CFRuleRecord)GetRule(i).Clone();
}
return new CFRecordsAggregate((CFHeaderRecord)header.Clone(), newRecs);
}
public override short Sid
{
get { return sid; }
}
///
/// called by the class that is responsible for writing this sucker.
/// Subclasses should implement this so that their data is passed back in a
/// byte array.
///
/// The offset to begin writing at
/// The data byte array containing instance data
/// number of bytes written
public override int Serialize(int offset, byte[] data)
{
int nRules = rules.Count;
header.NumberOfConditionalFormats = (nRules);
int pos = offset;
pos += header.Serialize(pos, data);
for (int i = 0; i < nRules; i++)
{
pos += GetRule(i).Serialize(pos, data);
}
return pos - offset;
}
public CFHeaderRecord Header
{
get { return header; }
}
private void CheckRuleIndex(int idx)
{
if (idx < 0 || idx >= rules.Count)
{
throw new ArgumentException("Bad rule record index (" + idx
+ ") nRules=" + rules.Count);
}
}
public CFRuleRecord GetRule(int idx)
{
CheckRuleIndex(idx);
return (CFRuleRecord)rules[idx];
}
public void SetRule(int idx, CFRuleRecord r)
{
CheckRuleIndex(idx);
rules[idx] = r;
}
/**
* @return false if this whole {@link CFHeaderRecord} / {@link CFRuleRecord}s should be deleted
*/
public bool UpdateFormulasAfterCellShift(FormulaShifter shifter, int currentExternSheetIx)
{
CellRangeAddress[] cellRanges = header.CellRanges;
bool changed = false;
ArrayList temp = new ArrayList();
for (int i = 0; i < cellRanges.Length; i++)
{
CellRangeAddress craOld = cellRanges[i];
CellRangeAddress craNew = ShiftRange(shifter, craOld, currentExternSheetIx);
if (craNew == null)
{
changed = true;
continue;
}
temp.Add(craNew);
if (craNew != craOld)
{
changed = true;
}
}
if (changed)
{
int nRanges = temp.Count;
if (nRanges == 0)
{
return false;
}
CellRangeAddress[] newRanges = new CellRangeAddress[nRanges];
newRanges = (CellRangeAddress[])temp.ToArray(typeof(CellRangeAddress));
header.CellRanges = (newRanges);
}
for (int i = 0; i < rules.Count; i++)
{
CFRuleRecord rule = (CFRuleRecord)rules[i];
Ptg[] ptgs;
ptgs = rule.ParsedExpression1;
if (ptgs != null && shifter.AdjustFormula(ptgs, currentExternSheetIx))
{
rule.ParsedExpression1 = (ptgs);
}
ptgs = rule.ParsedExpression2;
if (ptgs != null && shifter.AdjustFormula(ptgs, currentExternSheetIx))
{
rule.ParsedExpression2 = (ptgs);
}
}
return true;
}
private static CellRangeAddress ShiftRange(FormulaShifter shifter, CellRangeAddress cra, int currentExternSheetIx)
{
// FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here
AreaPtg aptg = new AreaPtg(cra.FirstRow, cra.LastRow, cra.FirstColumn, cra.LastColumn, false, false, false, false);
Ptg[] ptgs = { aptg, };
if (!shifter.AdjustFormula(ptgs, currentExternSheetIx))
{
return cra;
}
Ptg ptg0 = ptgs[0];
if (ptg0 is AreaPtg)
{
AreaPtg bptg = (AreaPtg)ptg0;
return new CellRangeAddress(bptg.FirstRow, bptg.LastRow, bptg.FirstColumn, bptg.LastColumn);
}
if (ptg0 is AreaErrPtg)
{
return null;
}
throw new InvalidCastException("Unexpected shifted ptg class (" + ptg0.GetType().Name + ")");
}
public void AddRule(CFRuleRecord r)
{
if (rules.Count >= MAX_CONDTIONAL_FORMAT_RULES)
{
throw new InvalidOperationException("Cannot have more than "
+ MAX_CONDTIONAL_FORMAT_RULES + " conditional format rules");
}
rules.Add(r);
header.NumberOfConditionalFormats = (rules.Count);
}
public int NumberOfRules
{
get { return rules.Count; }
}
/**
* @return sum of sizes of all aggregated records
*/
public override int RecordSize
{
get
{
int size = 0;
if (header != null)
{
size += header.RecordSize;
}
if (rules != null)
{
for (IEnumerator irecs = rules.GetEnumerator(); irecs.MoveNext(); )
{
size += ((Record)irecs.Current).RecordSize;
}
}
return size;
}
}
public override String ToString()
{
StringBuilder buffer = new StringBuilder();
buffer.Append("[CF]\n");
if (header != null)
{
buffer.Append(header.ToString());
}
for (int i = 0; i < rules.Count; i++)
{
CFRuleRecord cfRule = (CFRuleRecord)rules[i];
if (cfRule != null)
{
buffer.Append(cfRule.ToString());
}
}
buffer.Append("[/CF]\n");
return buffer.ToString();
}
}
}