git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1888939 13f79535-47bb-0310-9956-ffa450edef68tags/REL_5_1_0
return sxSheet; | return sxSheet; | ||||
} | } | ||||
@Override | |||||
public DeferredSXSSFSheet createSheet() { | public DeferredSXSSFSheet createSheet() { | ||||
return (DeferredSXSSFSheet) super.createSheet(); | return (DeferredSXSSFSheet) super.createSheet(); | ||||
} | } | ||||
@Override | |||||
public DeferredSXSSFSheet createSheet(String sheetname) { | public DeferredSXSSFSheet createSheet(String sheetname) { | ||||
return (DeferredSXSSFSheet) super.createSheet(sheetname); | return (DeferredSXSSFSheet) super.createSheet(sheetname); | ||||
} | } |
/** | /** | ||||
* Border line style. | * Border line style. | ||||
* | * | ||||
* @return One of | |||||
* @return One of <ul> | |||||
* <li>{@link #BRCL_SINGLE} | * <li>{@link #BRCL_SINGLE} | ||||
* <li>{@link #BRCL_THICK} | * <li>{@link #BRCL_THICK} | ||||
* <li>{@link #BRCL_DOUBLE} | * <li>{@link #BRCL_DOUBLE} | ||||
* <li>{@link #BRCL_SHADOW} | * <li>{@link #BRCL_SHADOW} | ||||
* </ul> | |||||
*/ | */ | ||||
@Internal | @Internal | ||||
public byte getBrcl() | public byte getBrcl() | ||||
/** | /** | ||||
* Border line style. | * Border line style. | ||||
* | * | ||||
* @param field_6_brcl | |||||
* One of | |||||
* @param field_6_brcl One of <ul> | |||||
* <li>{@link #BRCL_SINGLE} | * <li>{@link #BRCL_SINGLE} | ||||
* <li>{@link #BRCL_THICK} | * <li>{@link #BRCL_THICK} | ||||
* <li>{@link #BRCL_DOUBLE} | * <li>{@link #BRCL_DOUBLE} | ||||
* <li>{@link #BRCL_SHADOW} | * <li>{@link #BRCL_SHADOW} | ||||
* </ul> | |||||
*/ | */ | ||||
@Internal | @Internal | ||||
public void setBrcl( byte field_6_brcl ) | public void setBrcl( byte field_6_brcl ) | ||||
/** | /** | ||||
* Rectangle border codes. | * Rectangle border codes. | ||||
* | * | ||||
* @return One of | |||||
* @return One of <ul> | |||||
* <li>{@link #BRCP_NONE} | * <li>{@link #BRCP_NONE} | ||||
* <li>{@link #BRCP_BORDER_ABOVE} | * <li>{@link #BRCP_BORDER_ABOVE} | ||||
* <li>{@link #BRCP_BORDER_BELOW} | * <li>{@link #BRCP_BORDER_BELOW} | ||||
* <li>{@link #BRCP_BOX_AROUND} | * <li>{@link #BRCP_BOX_AROUND} | ||||
* <li>{@link #BRCP_BAR_TO_LEFT_OF_PARAGRAPH} | * <li>{@link #BRCP_BAR_TO_LEFT_OF_PARAGRAPH} | ||||
* </ul> | |||||
*/ | */ | ||||
@Internal | @Internal | ||||
public byte getBrcp() | public byte getBrcp() | ||||
/** | /** | ||||
* Rectangle border codes. | * Rectangle border codes. | ||||
* | * | ||||
* @param field_7_brcp | |||||
* One of | |||||
* @param field_7_brcp One of <ul> | |||||
* <li>{@link #BRCP_NONE} | * <li>{@link #BRCP_NONE} | ||||
* <li>{@link #BRCP_BORDER_ABOVE} | * <li>{@link #BRCP_BORDER_ABOVE} | ||||
* <li>{@link #BRCP_BORDER_BELOW} | * <li>{@link #BRCP_BORDER_BELOW} | ||||
* <li>{@link #BRCP_BOX_AROUND} | * <li>{@link #BRCP_BOX_AROUND} | ||||
* <li>{@link #BRCP_BAR_TO_LEFT_OF_PARAGRAPH} | * <li>{@link #BRCP_BAR_TO_LEFT_OF_PARAGRAPH} | ||||
* </ul> | |||||
*/ | */ | ||||
@Internal | @Internal | ||||
public void setBrcp( byte field_7_brcp ) | public void setBrcp( byte field_7_brcp ) | ||||
/** | /** | ||||
* Minimum height is exact or auto. | * Minimum height is exact or auto. | ||||
* | * | ||||
* @return One of | |||||
* @return One of <ul> | |||||
* <li>{@link #FMINHEIGHT_EXACT} | * <li>{@link #FMINHEIGHT_EXACT} | ||||
* <li>{@link #FMINHEIGHT_AT_LEAST} | * <li>{@link #FMINHEIGHT_AT_LEAST} | ||||
* </ul> | |||||
*/ | */ | ||||
@Internal | @Internal | ||||
public boolean getFMinHeight() | public boolean getFMinHeight() | ||||
/** | /** | ||||
* Minimum height is exact or auto. | * Minimum height is exact or auto. | ||||
* | * | ||||
* @param field_27_fMinHeight | |||||
* One of | |||||
* @param field_27_fMinHeight One of <ul> | |||||
* <li>{@link #FMINHEIGHT_EXACT} | * <li>{@link #FMINHEIGHT_EXACT} | ||||
* <li>{@link #FMINHEIGHT_AT_LEAST} | * <li>{@link #FMINHEIGHT_AT_LEAST} | ||||
* </ul> | |||||
*/ | */ | ||||
@Internal | @Internal | ||||
public void setFMinHeight( boolean field_27_fMinHeight ) | public void setFMinHeight( boolean field_27_fMinHeight ) | ||||
/** | /** | ||||
* Get the wAlignFont field for the PAP record. | * Get the wAlignFont field for the PAP record. | ||||
* | * | ||||
* @return One of | |||||
* @return One of <ul> | |||||
* <li>{@link #WALIGNFONT_HANGING} | * <li>{@link #WALIGNFONT_HANGING} | ||||
* <li>{@link #WALIGNFONT_CENTERED} | * <li>{@link #WALIGNFONT_CENTERED} | ||||
* <li>{@link #WALIGNFONT_ROMAN} | * <li>{@link #WALIGNFONT_ROMAN} | ||||
* <li>{@link #WALIGNFONT_VARIABLE} | * <li>{@link #WALIGNFONT_VARIABLE} | ||||
* <li>{@link #WALIGNFONT_AUTO} | * <li>{@link #WALIGNFONT_AUTO} | ||||
* </ul> | |||||
*/ | */ | ||||
@Internal | @Internal | ||||
public int getWAlignFont() | public int getWAlignFont() | ||||
/** | /** | ||||
* Set the wAlignFont field for the PAP record. | * Set the wAlignFont field for the PAP record. | ||||
* | * | ||||
* @param field_39_wAlignFont | |||||
* One of | |||||
* @param field_39_wAlignFont One of <ul> | |||||
* <li>{@link #WALIGNFONT_HANGING} | * <li>{@link #WALIGNFONT_HANGING} | ||||
* <li>{@link #WALIGNFONT_CENTERED} | * <li>{@link #WALIGNFONT_CENTERED} | ||||
* <li>{@link #WALIGNFONT_ROMAN} | * <li>{@link #WALIGNFONT_ROMAN} | ||||
* <li>{@link #WALIGNFONT_VARIABLE} | * <li>{@link #WALIGNFONT_VARIABLE} | ||||
* <li>{@link #WALIGNFONT_AUTO} | * <li>{@link #WALIGNFONT_AUTO} | ||||
* </ul> | |||||
*/ | */ | ||||
@Internal | @Internal | ||||
public void setWAlignFont( int field_39_wAlignFont ) | public void setWAlignFont( int field_39_wAlignFont ) |
public String toString() | public String toString() | ||||
{ | { | ||||
StringBuilder builder = new StringBuilder(); | |||||
builder.append("[SEP]\n"); | |||||
builder.append(" .bkc = "); | |||||
builder.append(" (").append(getBkc()).append(" )\n"); | |||||
builder.append(" .fTitlePage = "); | |||||
builder.append(" (").append(getFTitlePage()).append(" )\n"); | |||||
builder.append(" .fAutoPgn = "); | |||||
builder.append(" (").append(getFAutoPgn()).append(" )\n"); | |||||
builder.append(" .nfcPgn = "); | |||||
builder.append(" (").append(getNfcPgn()).append(" )\n"); | |||||
builder.append(" .fUnlocked = "); | |||||
builder.append(" (").append(getFUnlocked()).append(" )\n"); | |||||
builder.append(" .cnsPgn = "); | |||||
builder.append(" (").append(getCnsPgn()).append(" )\n"); | |||||
builder.append(" .fPgnRestart = "); | |||||
builder.append(" (").append(getFPgnRestart()).append(" )\n"); | |||||
builder.append(" .fEndNote = "); | |||||
builder.append(" (").append(getFEndNote()).append(" )\n"); | |||||
builder.append(" .lnc = "); | |||||
builder.append(" (").append(getLnc()).append(" )\n"); | |||||
builder.append(" .grpfIhdt = "); | |||||
builder.append(" (").append(getGrpfIhdt()).append(" )\n"); | |||||
builder.append(" .nLnnMod = "); | |||||
builder.append(" (").append(getNLnnMod()).append(" )\n"); | |||||
builder.append(" .dxaLnn = "); | |||||
builder.append(" (").append(getDxaLnn()).append(" )\n"); | |||||
builder.append(" .dxaPgn = "); | |||||
builder.append(" (").append(getDxaPgn()).append(" )\n"); | |||||
builder.append(" .dyaPgn = "); | |||||
builder.append(" (").append(getDyaPgn()).append(" )\n"); | |||||
builder.append(" .fLBetween = "); | |||||
builder.append(" (").append(getFLBetween()).append(" )\n"); | |||||
builder.append(" .vjc = "); | |||||
builder.append(" (").append(getVjc()).append(" )\n"); | |||||
builder.append(" .dmBinFirst = "); | |||||
builder.append(" (").append(getDmBinFirst()).append(" )\n"); | |||||
builder.append(" .dmBinOther = "); | |||||
builder.append(" (").append(getDmBinOther()).append(" )\n"); | |||||
builder.append(" .dmPaperReq = "); | |||||
builder.append(" (").append(getDmPaperReq()).append(" )\n"); | |||||
builder.append(" .brcTop = "); | |||||
builder.append(" (").append(getBrcTop()).append(" )\n"); | |||||
builder.append(" .brcLeft = "); | |||||
builder.append(" (").append(getBrcLeft()).append(" )\n"); | |||||
builder.append(" .brcBottom = "); | |||||
builder.append(" (").append(getBrcBottom()).append(" )\n"); | |||||
builder.append(" .brcRight = "); | |||||
builder.append(" (").append(getBrcRight()).append(" )\n"); | |||||
builder.append(" .fPropMark = "); | |||||
builder.append(" (").append(getFPropMark()).append(" )\n"); | |||||
builder.append(" .ibstPropRMark = "); | |||||
builder.append(" (").append(getIbstPropRMark()).append(" )\n"); | |||||
builder.append(" .dttmPropRMark = "); | |||||
builder.append(" (").append(getDttmPropRMark()).append(" )\n"); | |||||
builder.append(" .dxtCharSpace = "); | |||||
builder.append(" (").append(getDxtCharSpace()).append(" )\n"); | |||||
builder.append(" .dyaLinePitch = "); | |||||
builder.append(" (").append(getDyaLinePitch()).append(" )\n"); | |||||
builder.append(" .clm = "); | |||||
builder.append(" (").append(getClm()).append(" )\n"); | |||||
builder.append(" .unused2 = "); | |||||
builder.append(" (").append(getUnused2()).append(" )\n"); | |||||
builder.append(" .dmOrientPage = "); | |||||
builder.append(" (").append(getDmOrientPage()).append(" )\n"); | |||||
builder.append(" .iHeadingPgn = "); | |||||
builder.append(" (").append(getIHeadingPgn()).append(" )\n"); | |||||
builder.append(" .pgnStart = "); | |||||
builder.append(" (").append(getPgnStart()).append(" )\n"); | |||||
builder.append(" .lnnMin = "); | |||||
builder.append(" (").append(getLnnMin()).append(" )\n"); | |||||
builder.append(" .wTextFlow = "); | |||||
builder.append(" (").append(getWTextFlow()).append(" )\n"); | |||||
builder.append(" .unused3 = "); | |||||
builder.append(" (").append(getUnused3()).append(" )\n"); | |||||
builder.append(" .pgbProp = "); | |||||
builder.append(" (").append(getPgbProp()).append(" )\n"); | |||||
builder.append(" .unused4 = "); | |||||
builder.append(" (").append(getUnused4()).append(" )\n"); | |||||
builder.append(" .xaPage = "); | |||||
builder.append(" (").append(getXaPage()).append(" )\n"); | |||||
builder.append(" .yaPage = "); | |||||
builder.append(" (").append(getYaPage()).append(" )\n"); | |||||
builder.append(" .xaPageNUp = "); | |||||
builder.append(" (").append(getXaPageNUp()).append(" )\n"); | |||||
builder.append(" .yaPageNUp = "); | |||||
builder.append(" (").append(getYaPageNUp()).append(" )\n"); | |||||
builder.append(" .dxaLeft = "); | |||||
builder.append(" (").append(getDxaLeft()).append(" )\n"); | |||||
builder.append(" .dxaRight = "); | |||||
builder.append(" (").append(getDxaRight()).append(" )\n"); | |||||
builder.append(" .dyaTop = "); | |||||
builder.append(" (").append(getDyaTop()).append(" )\n"); | |||||
builder.append(" .dyaBottom = "); | |||||
builder.append(" (").append(getDyaBottom()).append(" )\n"); | |||||
builder.append(" .dzaGutter = "); | |||||
builder.append(" (").append(getDzaGutter()).append(" )\n"); | |||||
builder.append(" .dyaHdrTop = "); | |||||
builder.append(" (").append(getDyaHdrTop()).append(" )\n"); | |||||
builder.append(" .dyaHdrBottom = "); | |||||
builder.append(" (").append(getDyaHdrBottom()).append(" )\n"); | |||||
builder.append(" .ccolM1 = "); | |||||
builder.append(" (").append(getCcolM1()).append(" )\n"); | |||||
builder.append(" .fEvenlySpaced = "); | |||||
builder.append(" (").append(getFEvenlySpaced()).append(" )\n"); | |||||
builder.append(" .unused5 = "); | |||||
builder.append(" (").append(getUnused5()).append(" )\n"); | |||||
builder.append(" .dxaColumns = "); | |||||
builder.append(" (").append(getDxaColumns()).append(" )\n"); | |||||
builder.append(" .rgdxaColumn = "); | |||||
builder.append(" (").append(Arrays.toString(getRgdxaColumn())).append(" )\n"); | |||||
builder.append(" .dxaColumnWidth = "); | |||||
builder.append(" (").append(getDxaColumnWidth()).append(" )\n"); | |||||
builder.append(" .dmOrientFirst = "); | |||||
builder.append(" (").append(getDmOrientFirst()).append(" )\n"); | |||||
builder.append(" .fLayout = "); | |||||
builder.append(" (").append(getFLayout()).append(" )\n"); | |||||
builder.append(" .unused6 = "); | |||||
builder.append(" (").append(getUnused6()).append(" )\n"); | |||||
builder.append(" .olstAnm = "); | |||||
builder.append(" (").append(Arrays.toString(getOlstAnm())).append(" )\n"); | |||||
builder.append("[/SEP]\n"); | |||||
return builder.toString(); | |||||
return "[SEP]\n" + | |||||
" .bkc = (" + getBkc() + " )\n" + | |||||
" .fTitlePage = (" + getFTitlePage() + " )\n" + | |||||
" .fAutoPgn = (" + getFAutoPgn() + " )\n" + | |||||
" .nfcPgn = (" + getNfcPgn() + " )\n" + | |||||
" .fUnlocked = (" + getFUnlocked() + " )\n" + | |||||
" .cnsPgn = (" + getCnsPgn() + " )\n" + | |||||
" .fPgnRestart = (" + getFPgnRestart() + " )\n" + | |||||
" .fEndNote = (" + getFEndNote() + " )\n" + | |||||
" .lnc = (" + getLnc() + " )\n" + | |||||
" .grpfIhdt = (" + getGrpfIhdt() + " )\n" + | |||||
" .nLnnMod = (" + getNLnnMod() + " )\n" + | |||||
" .dxaLnn = (" + getDxaLnn() + " )\n" + | |||||
" .dxaPgn = (" + getDxaPgn() + " )\n" + | |||||
" .dyaPgn = (" + getDyaPgn() + " )\n" + | |||||
" .fLBetween = (" + getFLBetween() + " )\n" + | |||||
" .vjc = (" + getVjc() + " )\n" + | |||||
" .dmBinFirst = (" + getDmBinFirst() + " )\n" + | |||||
" .dmBinOther = (" + getDmBinOther() + " )\n" + | |||||
" .dmPaperReq = (" + getDmPaperReq() + " )\n" + | |||||
" .brcTop = (" + getBrcTop() + " )\n" + | |||||
" .brcLeft = (" + getBrcLeft() + " )\n" + | |||||
" .brcBottom = (" + getBrcBottom() + " )\n" + | |||||
" .brcRight = (" + getBrcRight() + " )\n" + | |||||
" .fPropMark = (" + getFPropMark() + " )\n" + | |||||
" .ibstPropRMark = (" + getIbstPropRMark() + " )\n" + | |||||
" .dttmPropRMark = (" + getDttmPropRMark() + " )\n" + | |||||
" .dxtCharSpace = (" + getDxtCharSpace() + " )\n" + | |||||
" .dyaLinePitch = (" + getDyaLinePitch() + " )\n" + | |||||
" .clm = (" + getClm() + " )\n" + | |||||
" .unused2 = (" + getUnused2() + " )\n" + | |||||
" .dmOrientPage = (" + getDmOrientPage() + " )\n" + | |||||
" .iHeadingPgn = (" + getIHeadingPgn() + " )\n" + | |||||
" .pgnStart = (" + getPgnStart() + " )\n" + | |||||
" .lnnMin = (" + getLnnMin() + " )\n" + | |||||
" .wTextFlow = (" + getWTextFlow() + " )\n" + | |||||
" .unused3 = (" + getUnused3() + " )\n" + | |||||
" .pgbProp = (" + getPgbProp() + " )\n" + | |||||
" .unused4 = (" + getUnused4() + " )\n" + | |||||
" .xaPage = (" + getXaPage() + " )\n" + | |||||
" .yaPage = (" + getYaPage() + " )\n" + | |||||
" .xaPageNUp = (" + getXaPageNUp() + " )\n" + | |||||
" .yaPageNUp = (" + getYaPageNUp() + " )\n" + | |||||
" .dxaLeft = (" + getDxaLeft() + " )\n" + | |||||
" .dxaRight = (" + getDxaRight() + " )\n" + | |||||
" .dyaTop = (" + getDyaTop() + " )\n" + | |||||
" .dyaBottom = (" + getDyaBottom() + " )\n" + | |||||
" .dzaGutter = (" + getDzaGutter() + " )\n" + | |||||
" .dyaHdrTop = (" + getDyaHdrTop() + " )\n" + | |||||
" .dyaHdrBottom = (" + getDyaHdrBottom() + " )\n" + | |||||
" .ccolM1 = (" + getCcolM1() + " )\n" + | |||||
" .fEvenlySpaced = (" + getFEvenlySpaced() + " )\n" + | |||||
" .unused5 = (" + getUnused5() + " )\n" + | |||||
" .dxaColumns = (" + getDxaColumns() + " )\n" + | |||||
" .rgdxaColumn = (" + Arrays.toString(getRgdxaColumn()) + " )\n" + | |||||
" .dxaColumnWidth = (" + getDxaColumnWidth() + " )\n" + | |||||
" .dmOrientFirst = (" + getDmOrientFirst() + " )\n" + | |||||
" .fLayout = (" + getFLayout() + " )\n" + | |||||
" .unused6 = (" + getUnused6() + " )\n" + | |||||
" .olstAnm = (" + Arrays.toString(getOlstAnm()) + " )\n" + | |||||
"[/SEP]\n"; | |||||
} | } | ||||
/** | /** | ||||
* Break code. | * Break code. | ||||
* | * | ||||
* @return One of | |||||
* @return One of <ul> | |||||
* <li>{@link #BKC_NO_BREAK} | * <li>{@link #BKC_NO_BREAK} | ||||
* <li>{@link #BKC_NEW_COLUMN} | * <li>{@link #BKC_NEW_COLUMN} | ||||
* <li>{@link #BKC_NEW_PAGE} | * <li>{@link #BKC_NEW_PAGE} | ||||
* <li>{@link #BKC_EVEN_PAGE} | * <li>{@link #BKC_EVEN_PAGE} | ||||
* <li>{@link #BKC_ODD_PAGE} | * <li>{@link #BKC_ODD_PAGE} | ||||
* </ul> | |||||
*/ | */ | ||||
public byte getBkc() | public byte getBkc() | ||||
{ | { | ||||
/** | /** | ||||
* Break code. | * Break code. | ||||
* | * | ||||
* @param field_1_bkc | |||||
* One of | |||||
* @param field_1_bkc One of <ul> | |||||
* <li>{@link #BKC_NO_BREAK} | * <li>{@link #BKC_NO_BREAK} | ||||
* <li>{@link #BKC_NEW_COLUMN} | * <li>{@link #BKC_NEW_COLUMN} | ||||
* <li>{@link #BKC_NEW_PAGE} | * <li>{@link #BKC_NEW_PAGE} | ||||
* <li>{@link #BKC_EVEN_PAGE} | * <li>{@link #BKC_EVEN_PAGE} | ||||
* <li>{@link #BKC_ODD_PAGE} | * <li>{@link #BKC_ODD_PAGE} | ||||
* </ul> | |||||
*/ | */ | ||||
public void setBkc(byte field_1_bkc) | public void setBkc(byte field_1_bkc) | ||||
{ | { | ||||
/** | /** | ||||
* Page number format code. | * Page number format code. | ||||
* | * | ||||
* @return One of | |||||
* @return One of <ul> | |||||
* <li>{@link #NFCPGN_ARABIC} | * <li>{@link #NFCPGN_ARABIC} | ||||
* <li>{@link #NFCPGN_ROMAN_UPPER_CASE} | * <li>{@link #NFCPGN_ROMAN_UPPER_CASE} | ||||
* <li>{@link #NFCPGN_ROMAN_LOWER_CASE} | * <li>{@link #NFCPGN_ROMAN_LOWER_CASE} | ||||
* <li>{@link #NFCPGN_LETTER_UPPER_CASE} | * <li>{@link #NFCPGN_LETTER_UPPER_CASE} | ||||
* <li>{@link #NFCPGN_LETTER_LOWER_CASE} | * <li>{@link #NFCPGN_LETTER_LOWER_CASE} | ||||
* </ul> | |||||
*/ | */ | ||||
public byte getNfcPgn() | public byte getNfcPgn() | ||||
{ | { | ||||
/** | /** | ||||
* Page number format code. | * Page number format code. | ||||
* | * | ||||
* @param field_4_nfcPgn | |||||
* One of | |||||
* @param field_4_nfcPgn One of <ul> | |||||
* <li>{@link #NFCPGN_ARABIC} | * <li>{@link #NFCPGN_ARABIC} | ||||
* <li>{@link #NFCPGN_ROMAN_UPPER_CASE} | * <li>{@link #NFCPGN_ROMAN_UPPER_CASE} | ||||
* <li>{@link #NFCPGN_ROMAN_LOWER_CASE} | * <li>{@link #NFCPGN_ROMAN_LOWER_CASE} | ||||
* <li>{@link #NFCPGN_LETTER_UPPER_CASE} | * <li>{@link #NFCPGN_LETTER_UPPER_CASE} | ||||
* <li>{@link #NFCPGN_LETTER_LOWER_CASE} | * <li>{@link #NFCPGN_LETTER_LOWER_CASE} | ||||
* </ul> | |||||
*/ | */ | ||||
public void setNfcPgn(byte field_4_nfcPgn) | public void setNfcPgn(byte field_4_nfcPgn) | ||||
{ | { | ||||
/** | /** | ||||
* Get the dmOrientPage field for the SEP record. | * Get the dmOrientPage field for the SEP record. | ||||
* | * | ||||
* @return One of | |||||
* @return One of <ul> | |||||
* <li>{@link #DMORIENTPAGE_LANDSCAPE} | * <li>{@link #DMORIENTPAGE_LANDSCAPE} | ||||
* <li>{@link #DMORIENTPAGE_PORTRAIT} | * <li>{@link #DMORIENTPAGE_PORTRAIT} | ||||
* </ul> | |||||
*/ | */ | ||||
public boolean getDmOrientPage() | public boolean getDmOrientPage() | ||||
{ | { | ||||
/** | /** | ||||
* Set the dmOrientPage field for the SEP record. | * Set the dmOrientPage field for the SEP record. | ||||
* | * | ||||
* @param field_31_dmOrientPage | |||||
* One of | |||||
* @param field_31_dmOrientPage One of <ul> | |||||
* <li>{@link #DMORIENTPAGE_LANDSCAPE} | * <li>{@link #DMORIENTPAGE_LANDSCAPE} | ||||
* <li>{@link #DMORIENTPAGE_PORTRAIT} | * <li>{@link #DMORIENTPAGE_PORTRAIT} | ||||
* </ul> | |||||
*/ | */ | ||||
public void setDmOrientPage(boolean field_31_dmOrientPage) | public void setDmOrientPage(boolean field_31_dmOrientPage) | ||||
{ | { |
/** | /** | ||||
* Some OPCPackages are packed in side an OLE2 container. | * Some OPCPackages are packed in side an OLE2 container. | ||||
* If encrypted, the {@link DirectoryNode} is called {@link Decryptor#DEFAULT_POIFS_ENTRY "EncryptedPackage"}, | * If encrypted, the {@link DirectoryNode} is called {@link Decryptor#DEFAULT_POIFS_ENTRY "EncryptedPackage"}, | ||||
* otherwise the node is called "Packge" | |||||
* otherwise the node is called "Package" | |||||
*/ | */ | ||||
public static final String OOXML_PACKAGE = "Package"; | public static final String OOXML_PACKAGE = "Package"; | ||||
* prefix or suffix. | * prefix or suffix. | ||||
* </p> | * </p> | ||||
* <p> | * <p> | ||||
* For example the Excel pattern <code>"$#,##0.00 "USD"_);($#,##0.00 "USD")" | |||||
* </code> will be correctly formatted as "$1,000.00 USD" or "($1,000.00 USD)". | |||||
* However the pattern <code>"00-00-00"</code> is incorrectly formatted by | |||||
* For example the Excel pattern {@code "$#,##0.00 "USD"_);($#,##0.00 "USD")" | |||||
* } will be correctly formatted as "$1,000.00 USD" or "($1,000.00 USD)". | |||||
* However the pattern {@code "00-00-00"} is incorrectly formatted by | |||||
* DecimalFormat as "000000--". For Excel formats that are not compatible with | * DecimalFormat as "000000--". For Excel formats that are not compatible with | ||||
* DecimalFormat, you can provide your own custom {@link Format} implementation | * DecimalFormat, you can provide your own custom {@link Format} implementation | ||||
* via <code>HSSFDataFormatter.addFormat(String,Format)</code>. The following | |||||
* via {@code HSSFDataFormatter.addFormat(String,Format)}. The following | |||||
* custom formats are already provided by this class: | * custom formats are already provided by this class: | ||||
* </p> | * </p> | ||||
* <pre> | |||||
* <ul><li>SSN "000-00-0000"</li> | * <ul><li>SSN "000-00-0000"</li> | ||||
* <li>Phone Number "(###) ###-####"</li> | * <li>Phone Number "(###) ###-####"</li> | ||||
* <li>Zip plus 4 "00000-0000"</li> | * <li>Zip plus 4 "00000-0000"</li> | ||||
* </ul> | * </ul> | ||||
* </pre> | |||||
* <p> | * <p> | ||||
* If the Excel format pattern cannot be parsed successfully, then a default | * If the Excel format pattern cannot be parsed successfully, then a default | ||||
* format will be used. The default number format will mimic the Excel General | * format will be used. The default number format will mimic the Excel General | ||||
* format: "#" for whole numbers and "#.##########" for decimal numbers. You | * format: "#" for whole numbers and "#.##########" for decimal numbers. You | ||||
* can override the default format pattern with <code> | |||||
* HSSFDataFormatter.setDefaultNumberFormat(Format)</code>. <b>Note:</b> the | |||||
* can override the default format pattern with {@code | |||||
* HSSFDataFormatter.setDefaultNumberFormat(Format)}. <b>Note:</b> the | |||||
* default format will only be used when a Format cannot be created from the | * default format will only be used when a Format cannot be created from the | ||||
* cell's data format string. | * cell's data format string. | ||||
*/ | */ |
* Note that Excel allows sheet names up to 31 chars in length but other applications | * Note that Excel allows sheet names up to 31 chars in length but other applications | ||||
* (such as OpenOffice) allow more. Some versions of Excel crash with names longer than 31 chars, | * (such as OpenOffice) allow more. Some versions of Excel crash with names longer than 31 chars, | ||||
* others - truncate such names to 31 character. | * others - truncate such names to 31 character. | ||||
* </p> | |||||
* <p> | * <p> | ||||
* POI's SpreadsheetAPI silently truncates the input argument to 31 characters. | * POI's SpreadsheetAPI silently truncates the input argument to 31 characters. | ||||
* Example: | * Example: | ||||
* assert 31 == sheet.getSheetName().length(); | * assert 31 == sheet.getSheetName().length(); | ||||
* assert "My very long sheet name which i" == sheet.getSheetName(); | * assert "My very long sheet name which i" == sheet.getSheetName(); | ||||
* }</pre> | * }</pre> | ||||
* </p> | |||||
* <p> | * <p> | ||||
* Except the 31-character constraint, Excel applies some other rules: | * Except the 31-character constraint, Excel applies some other rules: | ||||
* <p> | * <p> | ||||
* <li> closing square bracket (]) </li> | * <li> closing square bracket (]) </li> | ||||
* </ul> | * </ul> | ||||
* The string MUST NOT begin or end with the single quote (') character. | * The string MUST NOT begin or end with the single quote (') character. | ||||
* </p> | |||||
* | * | ||||
* @param sheetname sheetname to set for the sheet. | * @param sheetname sheetname to set for the sheet. | ||||
* @return Sheet representing the new sheet. | * @return Sheet representing the new sheet. |
/** | /** | ||||
* This class parses a formula string into a List of tokens in RPN order. | * This class parses a formula string into a List of tokens in RPN order. | ||||
* Inspired by | |||||
* Lets Build a Compiler, by Jack Crenshaw | |||||
* Inspired by "Lets Build a Compiler" by Jack Crenshaw | |||||
* BNF for the formula expression is : | * BNF for the formula expression is : | ||||
* <pre>{@code | |||||
* <expression> ::= <term> [<addop> <term>]* | * <expression> ::= <term> [<addop> <term>]* | ||||
* <term> ::= <factor> [ <mulop> <factor> ]* | * <term> ::= <factor> [ <mulop> <factor> ]* | ||||
* <factor> ::= <number> | (<expression>) | <cellRef> | <function> | * <factor> ::= <number> | (<expression>) | <cellRef> | <function> | ||||
* <function> ::= <functionName> ([expression [, expression]*]) | * <function> ::= <functionName> ([expression [, expression]*]) | ||||
* }</pre> | |||||
* <p> | * <p> | ||||
* For POI internal use only | * For POI internal use only | ||||
* <p> | |||||
*/ | */ | ||||
@Internal | @Internal | ||||
public final class FormulaParser { | public final class FormulaParser { | ||||
* @param workbook the parent workbook | * @param workbook the parent workbook | ||||
* @param formulaType the type of the formula | * @param formulaType the type of the formula | ||||
* @param sheetIndex the 0-based index of the sheet this formula belongs to. | * @param sheetIndex the 0-based index of the sheet this formula belongs to. | ||||
* The sheet index is required to resolve sheet-level names. <code>-1</code> means that | |||||
* The sheet index is required to resolve sheet-level names. {@code -1} means that | |||||
* the scope of the name will be ignored and the parser will match names only by name | * the scope of the name will be ignored and the parser will match names only by name | ||||
* @param rowIndex - the related cell's row index in 0-based form (-1 if the formula is not cell related) | * @param rowIndex - the related cell's row index in 0-based form (-1 if the formula is not cell related) | ||||
* used to handle structured references that have the "#This Row" quantifier. | * used to handle structured references that have the "#This Row" quantifier. | ||||
* @param workbook the parent workbook | * @param workbook the parent workbook | ||||
* @param formulaType the type of the formula | * @param formulaType the type of the formula | ||||
* @param sheetIndex the 0-based index of the sheet this formula belongs to. | * @param sheetIndex the 0-based index of the sheet this formula belongs to. | ||||
* The sheet index is required to resolve sheet-level names. <code>-1</code> means that | |||||
* The sheet index is required to resolve sheet-level names. {@code -1} means that | |||||
* the scope of the name will be ignored and the parser will match names only by name | * the scope of the name will be ignored and the parser will match names only by name | ||||
* | * | ||||
* @return array of parsed tokens | * @return array of parsed tokens | ||||
} | } | ||||
return (Area3DPxg) arr[0]; | return (Area3DPxg) arr[0]; | ||||
} | } | ||||
/** Read New Character From Input Stream */ | /** Read New Character From Input Stream */ | ||||
private void GetChar() { | private void GetChar() { | ||||
// The intersection operator is a space. We track whether the run of | |||||
// The intersection operator is a space. We track whether the run of | |||||
// whitespace preceding "look" counts as an intersection operator. | // whitespace preceding "look" counts as an intersection operator. | ||||
if (IsWhite(look)) { | if (IsWhite(look)) { | ||||
if (look == ' ') { | if (look == ' ') { | ||||
else { | else { | ||||
_inIntersection = false; | _inIntersection = false; | ||||
} | } | ||||
// Check to see if we've walked off the end of the string. | // Check to see if we've walked off the end of the string. | ||||
if (_pointer > _formulaLength) { | if (_pointer > _formulaLength) { | ||||
throw new RuntimeException("Parsed past the end of the formula, pos: " + _pointer + | throw new RuntimeException("Parsed past the end of the formula, pos: " + _pointer + | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
if (token instanceof OperandPtg) { | |||||
return false; | |||||
} | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @return <code>false</code> if sub-expression represented the specified ParseNode definitely | |||||
* @return {@code false} if sub-expression represented the specified ParseNode definitely | |||||
* cannot appear on either side of the range (':') operator | * cannot appear on either side of the range (':') operator | ||||
*/ | */ | ||||
private static boolean isValidRangeOperand(ParseNode a) { | private static boolean isValidRangeOperand(ParseNode a) { | ||||
} | } | ||||
// one special case of ScalarConstantPtg | // one special case of ScalarConstantPtg | ||||
if (tkn == ErrPtg.REF_INVALID) { | |||||
return true; | |||||
} | |||||
// All other ControlPtgs and ScalarConstantPtgs cannot be used with ':' | // All other ControlPtgs and ScalarConstantPtgs cannot be used with ':' | ||||
return false; | |||||
return tkn == ErrPtg.REF_INVALID; | |||||
} | } | ||||
/** | /** | ||||
SkipWhite(); | SkipWhite(); | ||||
int savePointer = _pointer; | int savePointer = _pointer; | ||||
SheetIdentifier sheetIden = parseSheetName(); | SheetIdentifier sheetIden = parseSheetName(); | ||||
if (sheetIden == null) { | if (sheetIden == null) { | ||||
resetPointer(savePointer); | resetPointer(savePointer); | ||||
} else { | } else { | ||||
if (part1 == null) { | if (part1 == null) { | ||||
if (sheetIden != null) { | if (sheetIden != null) { | ||||
if(look == '#'){ // error ref like MySheet!#REF! | if(look == '#'){ // error ref like MySheet!#REF! | ||||
return new ParseNode(ErrPtg.valueOf(parseErrorLiteral())); | |||||
return new ParseNode(ErrPtg.valueOf(parseErrorLiteral())); | |||||
} else { | } else { | ||||
// Is it a named range? | // Is it a named range? | ||||
String name = parseAsName(); | String name = parseAsName(); | ||||
} | } | ||||
private static final String specHeaders = "Headers"; | private static final String specHeaders = "Headers"; | ||||
private static final String specAll = "All"; | private static final String specAll = "All"; | ||||
private static final String specData = "Data"; | private static final String specData = "Data"; | ||||
private static final String specTotals = "Totals"; | private static final String specTotals = "Totals"; | ||||
private static final String specThisRow = "This Row"; | private static final String specThisRow = "This Row"; | ||||
/** | /** | ||||
* Parses a structured reference, returns it as area reference. | * Parses a structured reference, returns it as area reference. | ||||
* Examples: | * Examples: | ||||
* Table1[[#This Row], [col1]] | * Table1[[#This Row], [col1]] | ||||
* Table1[ [col1]:[col2] ] | * Table1[ [col1]:[col2] ] | ||||
* </pre> | * </pre> | ||||
* @param tableName | |||||
* @return Area Reference for the given table | * @return Area Reference for the given table | ||||
*/ | */ | ||||
private ParseNode parseStructuredReference(String tableName) { | private ParseNode parseStructuredReference(String tableName) { | ||||
if ( ! (_ssVersion.equals(SpreadsheetVersion.EXCEL2007)) ) { | if ( ! (_ssVersion.equals(SpreadsheetVersion.EXCEL2007)) ) { | ||||
throw new FormulaParseException("Structured references work only on XSSF (Excel 2007+)!"); | throw new FormulaParseException("Structured references work only on XSSF (Excel 2007+)!"); | ||||
} | } | ||||
throw new FormulaParseException("Illegal table name: '" + tableName + "'"); | throw new FormulaParseException("Illegal table name: '" + tableName + "'"); | ||||
} | } | ||||
String sheetName = tbl.getSheetName(); | String sheetName = tbl.getSheetName(); | ||||
int startCol = tbl.getStartColIndex(); | int startCol = tbl.getStartColIndex(); | ||||
int endCol = tbl.getEndColIndex(); | int endCol = tbl.getEndColIndex(); | ||||
int startRow = tbl.getStartRowIndex(); | int startRow = tbl.getStartRowIndex(); | ||||
int endRow = tbl.getEndRowIndex(); | int endRow = tbl.getEndRowIndex(); | ||||
// Do NOT return before done reading all the structured reference tokens from the input stream. | // Do NOT return before done reading all the structured reference tokens from the input stream. | ||||
// Throwing exceptions is okay. | // Throwing exceptions is okay. | ||||
int savePtr0 = _pointer; | int savePtr0 = _pointer; | ||||
GetChar(); | GetChar(); | ||||
boolean isTotalsSpec = false; | boolean isTotalsSpec = false; | ||||
boolean isThisRowSpec = false; | boolean isThisRowSpec = false; | ||||
boolean isDataSpec = false; | boolean isDataSpec = false; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if(nColQuantifiers == 0 && nSpecQuantifiers == 0){ | if(nColQuantifiers == 0 && nSpecQuantifiers == 0){ | ||||
resetPointer(savePtr0); | resetPointer(savePtr0); | ||||
savePtr0 = _pointer; | savePtr0 = _pointer; | ||||
return new ParseNode(ErrPtg.VALUE_INVALID); | return new ParseNode(ErrPtg.VALUE_INVALID); | ||||
} else { | } else { | ||||
throw new FormulaParseException( | throw new FormulaParseException( | ||||
"Formula contained [#This Row] or [@] structured reference but this row < 0. " + | |||||
"Formula contained [#This Row] or [@] structured reference but this row < 0. " + | |||||
"Row index must be specified for row-referencing structured references."); | "Row index must be specified for row-referencing structured references."); | ||||
} | } | ||||
} | } | ||||
int actualStartRow = startRow; | int actualStartRow = startRow; | ||||
int actualEndRow = endRow; | int actualEndRow = endRow; | ||||
int actualStartCol = startCol; | int actualStartCol = startCol; | ||||
int actualEndCol = endCol; | |||||
int actualEndCol = endCol; | |||||
if (nSpecQuantifiers > 0) { | if (nSpecQuantifiers > 0) { | ||||
//Selecting rows | //Selecting rows | ||||
if (nSpecQuantifiers == 1 && isAllSpec) { | if (nSpecQuantifiers == 1 && isAllSpec) { | ||||
actualStartRow = actualEndRow; | actualStartRow = actualEndRow; | ||||
} else if ((nSpecQuantifiers == 1 && isThisRowSpec) || isThisRow) { | } else if ((nSpecQuantifiers == 1 && isThisRowSpec) || isThisRow) { | ||||
actualStartRow = _rowIndex; //The rowNum is 0 based | actualStartRow = _rowIndex; //The rowNum is 0 based | ||||
actualEndRow = _rowIndex; | |||||
actualEndRow = _rowIndex; | |||||
} else { | } else { | ||||
throw new FormulaParseException("The formula "+ _formulaString + " is illegal"); | throw new FormulaParseException("The formula "+ _formulaString + " is illegal"); | ||||
} | } | ||||
} else { | } else { | ||||
if (isThisRow) { // there is a @ | if (isThisRow) { // there is a @ | ||||
actualStartRow = _rowIndex; //The rowNum is 0 based | actualStartRow = _rowIndex; //The rowNum is 0 based | ||||
actualEndRow = _rowIndex; | |||||
actualEndRow = _rowIndex; | |||||
} else { // Really no special quantifiers | } else { // Really no special quantifiers | ||||
actualStartRow++; | actualStartRow++; | ||||
if (tbl.getTotalsRowCount() > 0) actualEndRow--; | if (tbl.getTotalsRowCount() > 0) actualEndRow--; | ||||
int startIdx = tbl.findColumnIndex(startColumnName); | int startIdx = tbl.findColumnIndex(startColumnName); | ||||
int endIdx = tbl.findColumnIndex(endColumnName); | int endIdx = tbl.findColumnIndex(endColumnName); | ||||
if (startIdx == -1 || endIdx == -1) { | if (startIdx == -1 || endIdx == -1) { | ||||
throw new FormulaParseException("One of the columns "+ startColumnName +", "+ endColumnName +" doesn't exist in table "+ tbl.getName()); | |||||
} | |||||
throw new FormulaParseException("One of the columns "+ startColumnName +", "+ endColumnName +" doesn't exist in table "+ tbl.getName()); | |||||
} | |||||
actualStartCol = startCol+ startIdx; | actualStartCol = startCol+ startIdx; | ||||
actualEndCol = startCol + endIdx; | actualEndCol = startCol + endIdx; | ||||
} else if (nColQuantifiers == 1 && !isThisRow) { | } else if (nColQuantifiers == 1 && !isThisRow) { | ||||
if (startColumnName == null) { | if (startColumnName == null) { | ||||
throw new IllegalStateException("Fatal error"); | throw new IllegalStateException("Fatal error"); | ||||
Ptg ptg = _book.get3DReferencePtg(new AreaReference(topLeft, bottomRight, _ssVersion), sheetIden); | Ptg ptg = _book.get3DReferencePtg(new AreaReference(topLeft, bottomRight, _ssVersion), sheetIden); | ||||
return new ParseNode(ptg); | return new ParseNode(ptg); | ||||
} | } | ||||
/** | /** | ||||
* Tries to parse the next as column - can contain whitespace | * Tries to parse the next as column - can contain whitespace | ||||
* Caller should save pointer. | * Caller should save pointer. | ||||
} | } | ||||
Match(']'); | Match(']'); | ||||
return name.toString(); | return name.toString(); | ||||
} | |||||
} | |||||
/** | /** | ||||
* Tries to parse the next as special quantifier | * Tries to parse the next as special quantifier | ||||
* Caller should save pointer. | * Caller should save pointer. | ||||
if (look == '"') { | if (look == '"') { | ||||
return new ParseNode(new StringPtg(parseStringLiteral())); | return new ParseNode(new StringPtg(parseStringLiteral())); | ||||
} | } | ||||
// from now on we can only be dealing with non-quoted identifiers | // from now on we can only be dealing with non-quoted identifiers | ||||
// which will either be named ranges or functions | // which will either be named ranges or functions | ||||
String name = parseAsName(); | String name = parseAsName(); | ||||
throw new FormulaParseException("Specified name '" | throw new FormulaParseException("Specified name '" | ||||
+ name + "' is not a range as expected."); | + name + "' is not a range as expected."); | ||||
} | } | ||||
private String parseAsName() { | private String parseAsName() { | ||||
StringBuilder sb = new StringBuilder(); | StringBuilder sb = new StringBuilder(); | ||||
/** | /** | ||||
* @param ch unicode codepoint | * @param ch unicode codepoint | ||||
* @return <code>true</code> if the specified character may be used in a defined name | |||||
* @return {@code true} if the specified character may be used in a defined name | |||||
*/ | */ | ||||
private static boolean isValidDefinedNameChar(int ch) { | private static boolean isValidDefinedNameChar(int ch) { | ||||
if (Character.isLetterOrDigit(ch)) { | if (Character.isLetterOrDigit(ch)) { | ||||
// includes special non-name control characters like ! $ : , ( ) [ ] and space | // includes special non-name control characters like ! $ : , ( ) [ ] and space | ||||
return false; | return false; | ||||
} | } | ||||
/** | /** | ||||
* | * | ||||
* @param sheetIden may be <code>null</code> | |||||
* @param part1 | |||||
* @param part2 may be <code>null</code> | |||||
* @param sheetIden may be {@code null} | |||||
* @param part2 may be {@code null} | |||||
*/ | */ | ||||
private ParseNode createAreaRefParseNode(SheetIdentifier sheetIden, SimpleRangePart part1, | private ParseNode createAreaRefParseNode(SheetIdentifier sheetIden, SimpleRangePart part1, | ||||
SimpleRangePart part2) throws FormulaParseException { | SimpleRangePart part2) throws FormulaParseException { | ||||
/** | /** | ||||
* Matches a zero or one letter-runs followed by zero or one digit-runs. | * Matches a zero or one letter-runs followed by zero or one digit-runs. | ||||
* Either or both runs man optionally be prefixed with a single '$'. | * Either or both runs man optionally be prefixed with a single '$'. | ||||
* (copied+modified from {@link CellReference#CELL_REF_PATTERN}) | |||||
* (copied+modified from CellReference.CELL_REF_PATTERN) | |||||
*/ | */ | ||||
private static final Pattern CELL_REF_PATTERN = Pattern.compile("(\\$?[A-Za-z]+)?(\\$?[0-9]+)?"); | private static final Pattern CELL_REF_PATTERN = Pattern.compile("(\\$?[A-Za-z]+)?(\\$?[0-9]+)?"); | ||||
/** | /** | ||||
* Parses out a potential LHS or RHS of a ':' intended to produce a plain AreaRef. Normally these are | * Parses out a potential LHS or RHS of a ':' intended to produce a plain AreaRef. Normally these are | ||||
* proper cell references but they could also be row or column refs like "$AC" or "10" | * proper cell references but they could also be row or column refs like "$AC" or "10" | ||||
* @return <code>null</code> (and leaves {@link #_pointer} unchanged if a proper range part does not parse out | |||||
* @return {@code null} (and leaves {@link #_pointer} unchanged if a proper range part does not parse out | |||||
*/ | */ | ||||
private SimpleRangePart parseSimpleRangePart() { | private SimpleRangePart parseSimpleRangePart() { | ||||
int ptr = _pointer-1; // TODO avoid StringIndexOutOfBounds | int ptr = _pointer-1; // TODO avoid StringIndexOutOfBounds | ||||
} | } | ||||
/** | /** | ||||
* @return <code>true</code> if the two range parts can be combined in an | |||||
* @return {@code true} if the two range parts can be combined in an | |||||
* {@link AreaPtg} ( Note - the explicit range operator (:) may still be valid | * {@link AreaPtg} ( Note - the explicit range operator (:) may still be valid | ||||
* when this method returns <code>false</code> ) | |||||
* when this method returns {@code false} ) | |||||
*/ | */ | ||||
public boolean isCompatibleForArea(SimpleRangePart part2) { | public boolean isCompatibleForArea(SimpleRangePart part2) { | ||||
return _type == part2._type; | return _type == part2._type; | ||||
return getClass().getName() + " [" + _rep + "]"; | return getClass().getName() + " [" + _rep + "]"; | ||||
} | } | ||||
} | } | ||||
private String getBookName() { | private String getBookName() { | ||||
StringBuilder sb = new StringBuilder(); | StringBuilder sb = new StringBuilder(); | ||||
GetChar(); | GetChar(); | ||||
} | } | ||||
/** | /** | ||||
* Note - caller should reset {@link #_pointer} upon <code>null</code> result | |||||
* @return The sheet name as an identifier <code>null</code> if '!' is not found in the right place | |||||
* Note - caller should reset {@link #_pointer} upon {@code null} result | |||||
* @return The sheet name as an identifier {@code null} if '!' is not found in the right place | |||||
*/ | */ | ||||
private SheetIdentifier parseSheetName() { | private SheetIdentifier parseSheetName() { | ||||
String bookName; | String bookName; | ||||
if (look == '\'') { | if (look == '\'') { | ||||
Match('\''); | Match('\''); | ||||
if (look == '[') | if (look == '[') | ||||
bookName = getBookName(); | bookName = getBookName(); | ||||
StringBuilder sb = new StringBuilder(); | StringBuilder sb = new StringBuilder(); | ||||
boolean done = look == '\''; | boolean done = look == '\''; | ||||
while(!done) { | while(!done) { | ||||
} | } | ||||
return null; | return null; | ||||
} | } | ||||
/** | /** | ||||
* If we have something that looks like [book]Sheet1: or | |||||
* If we have something that looks like [book]Sheet1: or | |||||
* Sheet1, see if it's actually a range eg Sheet1:Sheet2! | * Sheet1, see if it's actually a range eg Sheet1:Sheet2! | ||||
*/ | */ | ||||
private SheetIdentifier parseSheetRange(String bookname, NameIdentifier sheet1Name) { | private SheetIdentifier parseSheetRange(String bookname, NameIdentifier sheet1Name) { | ||||
} | } | ||||
/** | /** | ||||
* @return <code>true</code> if the specified name is a valid cell reference | |||||
* @return {@code true} if the specified name is a valid cell reference | |||||
*/ | */ | ||||
private boolean isValidCellReference(String str) { | private boolean isValidCellReference(String str) { | ||||
//check range bounds against grid max | //check range bounds against grid max | ||||
* Note - Excel function names are 'case aware but not case sensitive'. This method may end | * Note - Excel function names are 'case aware but not case sensitive'. This method may end | ||||
* up creating a defined name record in the workbook if the specified name is not an internal | * up creating a defined name record in the workbook if the specified name is not an internal | ||||
* Excel function, and has not been encountered before. | * Excel function, and has not been encountered before. | ||||
* | |||||
* | |||||
* Side effect: creates workbook name if name is not recognized (name is probably a UDF) | * Side effect: creates workbook name if name is not recognized (name is probably a UDF) | ||||
* | * | ||||
* @param name case preserved function name (as it was entered/appeared in the formula). | * @param name case preserved function name (as it was entered/appeared in the formula). | ||||
throw new FormulaParseException("Attempt to use name '" + name | throw new FormulaParseException("Attempt to use name '" + name | ||||
+ "' as a function, but defined name in workbook does not refer to a function"); | + "' as a function, but defined name in workbook does not refer to a function"); | ||||
} | } | ||||
// calls to user-defined functions within the workbook | // calls to user-defined functions within the workbook | ||||
// get a Name token which points to a defined name record | // get a Name token which points to a defined name record | ||||
nameToken = hName.createPtg(); | nameToken = hName.createPtg(); | ||||
return getFunction(name, nameToken, args); | return getFunction(name, nameToken, args); | ||||
} | } | ||||
/** | /** | ||||
* Adds a name (named range or user defined function) to underlying workbook's names table | * Adds a name (named range or user defined function) to underlying workbook's names table | ||||
*/ | */ | ||||
* Generates the variable function ptg for the formula. | * Generates the variable function ptg for the formula. | ||||
* <p> | * <p> | ||||
* For IF Formulas, additional PTGs are added to the tokens | * For IF Formulas, additional PTGs are added to the tokens | ||||
* @param name a {@link NamePtg} or {@link NameXPtg} or <code>null</code> | |||||
* @param name a {@link NamePtg} or {@link NameXPtg} or {@code null} | |||||
* @return Ptg a null is returned if we're in an IF formula, it needs extreme manipulation and is handled in this function | * @return Ptg a null is returned if we're in an IF formula, it needs extreme manipulation and is handled in this function | ||||
*/ | */ | ||||
private ParseNode getFunction(String name, Ptg namePtg, ParseNode[] args) { | private ParseNode getFunction(String name, Ptg namePtg, ParseNode[] args) { | ||||
if (!isPositive) { | if (!isPositive) { | ||||
value = -value; | value = -value; | ||||
} | } | ||||
return Double.valueOf(value); | |||||
return value; | |||||
} | } | ||||
private Ptg parseNumber() { | private Ptg parseNumber() { | ||||
return result; | return result; | ||||
} | } | ||||
} | } | ||||
private ParseNode comparisonExpression() { | private ParseNode comparisonExpression() { | ||||
ParseNode result = concatExpression(); | ParseNode result = concatExpression(); | ||||
while (true) { | while (true) { |
* | * | ||||
* The <b>basis</b> optionally specifies the behaviour of YEARFRAC as follows: | * The <b>basis</b> optionally specifies the behaviour of YEARFRAC as follows: | ||||
* | * | ||||
* <table border="0" cellpadding="1" cellspacing="0" summary="basis parameter description"> | |||||
* <table> | |||||
* <caption>basis parameter description</caption> | |||||
* <tr><th>Value</th><th>Days per Month</th><th>Days per Year</th></tr> | * <tr><th>Value</th><th>Days per Month</th><th>Days per Year</th></tr> | ||||
* <tr align='center'><td>0 (default)</td><td>30</td><td>360</td></tr> | * <tr align='center'><td>0 (default)</td><td>30</td><td>360</td></tr> | ||||
* <tr align='center'><td>1</td><td>actual</td><td>actual</td></tr> | * <tr align='center'><td>1</td><td>actual</td><td>actual</td></tr> | ||||
// enforce singleton | // enforce singleton | ||||
} | } | ||||
@Override | |||||
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { | public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { | ||||
int srcCellRow = ec.getRowIndex(); | int srcCellRow = ec.getRowIndex(); | ||||
int srcCellCol = ec.getColumnIndex(); | int srcCellCol = ec.getColumnIndex(); | ||||
String strVal = ((StringEval) ve).getStringValue(); | String strVal = ((StringEval) ve).getStringValue(); | ||||
Double dVal = OperandResolver.parseDouble(strVal); | Double dVal = OperandResolver.parseDouble(strVal); | ||||
if (dVal != null) { | if (dVal != null) { | ||||
return dVal.doubleValue(); | |||||
return dVal; | |||||
} | } | ||||
LocalDate date = DateParser.parseLocalDate(strVal); | LocalDate date = DateParser.parseLocalDate(strVal); | ||||
return DateUtil.getExcelDate(date, false); | return DateUtil.getExcelDate(date, false); |
* area depending on the coordinates of the calling cell. Here is an example demonstrating | * area depending on the coordinates of the calling cell. Here is an example demonstrating | ||||
* both selection from a single row area and a single column area in the same formula. | * both selection from a single row area and a single column area in the same formula. | ||||
* | * | ||||
* <table border="1" cellpadding="1" cellspacing="1" summary="sample spreadsheet"> | |||||
* <table> | |||||
* <caption>sample spreadsheet</caption> | |||||
* <tr><th> </th><th> A </th><th> B </th><th> C </th><th> D </th></tr> | * <tr><th> </th><th> A </th><th> B </th><th> C </th><th> D </th></tr> | ||||
* <tr><th>1</th><td>15</td><td>20</td><td>25</td><td> </td></tr> | * <tr><th>1</th><td>15</td><td>20</td><td>25</td><td> </td></tr> | ||||
* <tr><th>2</th><td> </td><td> </td><td> </td><td>200</td></tr> | * <tr><th>2</th><td> </td><td> </td><td> </td><td>200</td></tr> | ||||
* If the formula "=1000+A1:B1+D2:D3" is put into the 9 cells from A2 to C4, the spreadsheet | * If the formula "=1000+A1:B1+D2:D3" is put into the 9 cells from A2 to C4, the spreadsheet | ||||
* will look like this: | * will look like this: | ||||
* | * | ||||
* <table border="1" cellpadding="1" cellspacing="1" summary="sample spreadsheet"> | |||||
* <table> | |||||
* <caption>sample spreadsheet</caption> | |||||
* <tr><th> </th><th> A </th><th> B </th><th> C </th><th> D </th></tr> | * <tr><th> </th><th> A </th><th> B </th><th> C </th><th> D </th></tr> | ||||
* <tr><th>1</th><td>15</td><td>20</td><td>25</td><td> </td></tr> | * <tr><th>1</th><td>15</td><td>20</td><td>25</td><td> </td></tr> | ||||
* <tr><th>2</th><td>1215</td><td>1220</td><td>#VALUE!</td><td>200</td></tr> | * <tr><th>2</th><td>1215</td><td>1220</td><td>#VALUE!</td><td>200</td></tr> |
* Implementation for the function COUNTBLANK | * Implementation for the function COUNTBLANK | ||||
* <p> | * <p> | ||||
* Syntax: COUNTBLANK ( range ) | * Syntax: COUNTBLANK ( range ) | ||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions"> | |||||
* <table> | |||||
* <caption>Parameter descriptions</caption> | |||||
* <tr><th>range </th><td>is the range of cells to count blanks</td></tr> | * <tr><th>range </th><td>is the range of cells to count blanks</td></tr> | ||||
* </table> | * </table> | ||||
* </p> | * </p> | ||||
*/ | */ | ||||
public final class Countblank extends Fixed1ArgFunction { | public final class Countblank extends Fixed1ArgFunction { | ||||
@Override | |||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { | public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { | ||||
double result; | double result; | ||||
return new NumberEval(result); | return new NumberEval(result); | ||||
} | } | ||||
private static final I_MatchPredicate predicate = new I_MatchPredicate() { | |||||
public boolean matches(ValueEval valueEval) { | |||||
// Note - only BlankEval counts | |||||
return valueEval == BlankEval.instance || | |||||
// see https://support.office.com/en-us/article/COUNTBLANK-function-6a92d772-675c-4bee-b346-24af6bd3ac22 | |||||
// "Cells with formulas that return "" (empty text) are also counted." | |||||
(valueEval instanceof StringEval && ((StringEval)valueEval).getStringValue().isEmpty()); | |||||
} | |||||
private static final I_MatchPredicate predicate = valueEval -> { | |||||
// Note - only BlankEval counts | |||||
return valueEval == BlankEval.instance || | |||||
// see https://support.office.com/en-us/article/COUNTBLANK-function-6a92d772-675c-4bee-b346-24af6bd3ac22 | |||||
// "Cells with formulas that return "" (empty text) are also counted." | |||||
(valueEval instanceof StringEval && ((StringEval)valueEval).getStringValue().isEmpty()); | |||||
}; | }; | ||||
} | } |
* Implementation for the function COUNTIF | * Implementation for the function COUNTIF | ||||
* <p> | * <p> | ||||
* Syntax: COUNTIF ( range, criteria ) | * Syntax: COUNTIF ( range, criteria ) | ||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions"> | |||||
* <table> | |||||
* <caption>Parameter descriptions</caption> | |||||
* <tr><th>range </th><td>is the range of cells to be counted based on the criteria</td></tr> | * <tr><th>range </th><td>is the range of cells to be counted based on the criteria</td></tr> | ||||
* <tr><th>criteria</th><td>is used to determine which cells to count</td></tr> | * <tr><th>criteria</th><td>is used to determine which cells to count</td></tr> | ||||
* </table> | * </table> | ||||
* </p> | |||||
*/ | */ | ||||
public final class Countif extends Fixed2ArgFunction { | public final class Countif extends Fixed2ArgFunction { | ||||
} | } | ||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
StringBuilder sb = new StringBuilder(64); | |||||
sb.append(getClass().getName()); | |||||
sb.append(" [").append(_representation).append("]"); | |||||
return sb.toString(); | |||||
return getClass().getName() + " [" + _representation + "]"; | |||||
} | } | ||||
public String getRepresentation() { | public String getRepresentation() { | ||||
return _representation; | return _representation; | ||||
// x is text that is not a number | // x is text that is not a number | ||||
return false; | return false; | ||||
} | } | ||||
return _value == val.doubleValue(); | |||||
return _value == val; | |||||
} else if((x instanceof NumberEval)) { | } else if((x instanceof NumberEval)) { | ||||
NumberEval ne = (NumberEval) x; | NumberEval ne = (NumberEval) x; | ||||
testValue = ne.getNumberValue(); | testValue = ne.getNumberValue(); | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
public int getValue() { | public int getValue() { | ||||
return _value; | return _value; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Translates Excel countif wildcard strings into java regex strings | * Translates Excel countif wildcard strings into java regex strings | ||||
* @return <code>null</code> if the specified value contains no special wildcard characters. | |||||
* @return {@code null} if the specified value contains no special wildcard characters. | |||||
*/ | */ | ||||
public static Pattern getWildCardPattern(String value) { | public static Pattern getWildCardPattern(String value) { | ||||
int len = value.length(); | int len = value.length(); | ||||
/** | /** | ||||
* Creates a criteria predicate object for the supplied criteria arg | * Creates a criteria predicate object for the supplied criteria arg | ||||
* @return <code>null</code> if the arg evaluates to blank. | |||||
* @return {@code null} if the arg evaluates to blank. | |||||
*/ | */ | ||||
/* package */ static I_MatchPredicate createCriteriaPredicate(ValueEval arg, int srcRowIndex, int srcColumnIndex) { | /* package */ static I_MatchPredicate createCriteriaPredicate(ValueEval arg, int srcRowIndex, int srcColumnIndex) { | ||||
Boolean booleanVal = parseBoolean(value); | Boolean booleanVal = parseBoolean(value); | ||||
if(booleanVal != null) { | if(booleanVal != null) { | ||||
return new BooleanMatcher(booleanVal.booleanValue(), operator); | |||||
return new BooleanMatcher(booleanVal, operator); | |||||
} | } | ||||
Double doubleVal = OperandResolver.parseDouble(value); | Double doubleVal = OperandResolver.parseDouble(value); | ||||
if(doubleVal != null) { | if(doubleVal != null) { | ||||
return new NumberMatcher(doubleVal.doubleValue(), operator); | |||||
return new NumberMatcher(doubleVal, operator); | |||||
} | } | ||||
ErrorEval ee = parseError(value); | ErrorEval ee = parseError(value); | ||||
if (ee != null) { | if (ee != null) { | ||||
} | } | ||||
/** | /** | ||||
* Boolean literals ('TRUE', 'FALSE') treated similarly but NOT same as numbers. | * Boolean literals ('TRUE', 'FALSE') treated similarly but NOT same as numbers. | ||||
* @return <code>null</code> to represent blank values | |||||
* @return {@code null} to represent blank values | |||||
*/ | */ | ||||
/* package */ static Boolean parseBoolean(String strRep) { | /* package */ static Boolean parseBoolean(String strRep) { | ||||
if (strRep.length() < 1) { | if (strRep.length() < 1) { |
* <b>ERROR.TYPE</b>(<b>errorValue</b>)</p> | * <b>ERROR.TYPE</b>(<b>errorValue</b>)</p> | ||||
* <p> | * <p> | ||||
* Returns a number corresponding to the error type of the supplied argument. | * Returns a number corresponding to the error type of the supplied argument. | ||||
* <table border="1" cellpadding="1" cellspacing="1" summary="Return values for ERROR.TYPE()"> | |||||
* <table> | |||||
* <caption>Return values for ERROR.TYPE()</caption> | |||||
* <tr><td>errorValue</td><td>Return Value</td></tr> | * <tr><td>errorValue</td><td>Return Value</td></tr> | ||||
* <tr><td>#NULL!</td><td>1</td></tr> | * <tr><td>#NULL!</td><td>1</td></tr> | ||||
* <tr><td>#DIV/0!</td><td>2</td></tr> | * <tr><td>#DIV/0!</td><td>2</td></tr> |
* Syntax : <p> | * Syntax : <p> | ||||
* INDEX ( reference, row_num[, column_num [, area_num]])<p> | * INDEX ( reference, row_num[, column_num [, area_num]])<p> | ||||
* INDEX ( array, row_num[, column_num]) | * INDEX ( array, row_num[, column_num]) | ||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions"> | |||||
* <table> | |||||
* <caption>Parameter descriptions</caption> | |||||
* <tr><th>reference</th><td>typically an area reference, possibly a union of areas</td></tr> | * <tr><th>reference</th><td>typically an area reference, possibly a union of areas</td></tr> | ||||
* <tr><th>array</th><td>a literal array value (currently not supported)</td></tr> | * <tr><th>array</th><td>a literal array value (currently not supported)</td></tr> | ||||
* <tr><th>row_num</th><td>selects the row within the array or area reference</td></tr> | * <tr><th>row_num</th><td>selects the row within the array or area reference</td></tr> | ||||
*/ | */ | ||||
public final class Index implements Function2Arg, Function3Arg, Function4Arg, ArrayMode { | public final class Index implements Function2Arg, Function3Arg, Function4Arg, ArrayMode { | ||||
@Override | |||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { | public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { | ||||
TwoDEval reference = convertFirstArg(arg0); | TwoDEval reference = convertFirstArg(arg0); | ||||
return e.getErrorEval(); | return e.getErrorEval(); | ||||
} | } | ||||
} | } | ||||
@Override | |||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, | public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, | ||||
ValueEval arg2) { | ValueEval arg2) { | ||||
TwoDEval reference = convertFirstArg(arg0); | TwoDEval reference = convertFirstArg(arg0); | ||||
return e.getErrorEval(); | return e.getErrorEval(); | ||||
} | } | ||||
} | } | ||||
@Override | |||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, | public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, | ||||
ValueEval arg2, ValueEval arg3) { | ValueEval arg2, ValueEval arg3) { | ||||
throw new RuntimeException("Incomplete code" | throw new RuntimeException("Incomplete code" | ||||
} | } | ||||
@Override | |||||
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { | public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { | ||||
switch (args.length) { | switch (args.length) { | ||||
case 2: | case 2: |
* | * | ||||
* INDIRECT() returns the cell or area reference denoted by the text argument.<p> | * INDIRECT() returns the cell or area reference denoted by the text argument.<p> | ||||
* | * | ||||
* <b>Syntax</b>:</br> | |||||
* <b>Syntax</b>:<br> | |||||
* <b>INDIRECT</b>(<b>ref_text</b>,isA1Style)<p> | * <b>INDIRECT</b>(<b>ref_text</b>,isA1Style)<p> | ||||
* | * | ||||
* <b>ref_text</b> a string representation of the desired reference as it would | * <b>ref_text</b> a string representation of the desired reference as it would | ||||
// enforce singleton | // enforce singleton | ||||
} | } | ||||
@Override | |||||
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { | public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { | ||||
if (args.length < 1) { | if (args.length < 1) { | ||||
return ErrorEval.VALUE_INVALID; | return ErrorEval.VALUE_INVALID; | ||||
} | } | ||||
// numeric quantities follow standard boolean conversion rules | // numeric quantities follow standard boolean conversion rules | ||||
// for strings, only "TRUE" and "FALSE" (case insensitive) are valid | // for strings, only "TRUE" and "FALSE" (case insensitive) are valid | ||||
return OperandResolver.coerceValueToBoolean(ve, false).booleanValue(); | |||||
return OperandResolver.coerceValueToBoolean(ve, false); | |||||
} | } | ||||
private static ValueEval evaluateIndirect(final OperationEvaluationContext ec, String text, | private static ValueEval evaluateIndirect(final OperationEvaluationContext ec, String text, | ||||
if (Table.isStructuredReference.matcher(refText).matches()) { | if (Table.isStructuredReference.matcher(refText).matches()) { | ||||
// The argument is structured reference | // The argument is structured reference | ||||
Area3DPxg areaPtg = null; | |||||
Area3DPxg areaPtg; | |||||
try { | try { | ||||
areaPtg = FormulaParser.parseStructuredReference(refText, (FormulaParsingWorkbook) ec.getWorkbook(), ec.getRowIndex()); | areaPtg = FormulaParser.parseStructuredReference(refText, (FormulaParsingWorkbook) ec.getWorkbook(), ec.getRowIndex()); | ||||
} catch (FormulaParseException e) { | } catch (FormulaParseException e) { | ||||
/** | /** | ||||
* @return array of length 2: {workbookName, sheetName,}. Second element will always be | * @return array of length 2: {workbookName, sheetName,}. Second element will always be | ||||
* present. First element may be null if sheetName is unqualified. | * present. First element may be null if sheetName is unqualified. | ||||
* Returns <code>null</code> if text cannot be parsed. | |||||
* Returns {@code null} if text cannot be parsed. | |||||
*/ | */ | ||||
private static String[] parseWorkbookAndSheetName(CharSequence text) { | private static String[] parseWorkbookAndSheetName(CharSequence text) { | ||||
int lastIx = text.length() - 1; | int lastIx = text.length() - 1; | ||||
} | } | ||||
/** | /** | ||||
* @return <code>null</code> if there is a syntax error in any escape sequence | |||||
* @return {@code null} if there is a syntax error in any escape sequence | |||||
* (the typical syntax error is a single quote character not followed by another). | * (the typical syntax error is a single quote character not followed by another). | ||||
*/ | */ | ||||
private static String unescapeString(CharSequence text) { | private static String unescapeString(CharSequence text) { | ||||
if (Character.isWhitespace(text.charAt(0))) { | if (Character.isWhitespace(text.charAt(0))) { | ||||
return true; | return true; | ||||
} | } | ||||
if (Character.isWhitespace(text.charAt(lastIx))) { | |||||
return true; | |||||
} | |||||
return false; | |||||
return Character.isWhitespace(text.charAt(lastIx)); | |||||
} | } | ||||
} | } |
* Processes the third argument to VLOOKUP, or HLOOKUP (<b>col_index_num</b> | * Processes the third argument to VLOOKUP, or HLOOKUP (<b>col_index_num</b> | ||||
* or <b>row_index_num</b> respectively).<br> | * or <b>row_index_num</b> respectively).<br> | ||||
* Sample behaviour: | * Sample behaviour: | ||||
* <table border="0" cellpadding="1" cellspacing="2" summary="Sample behaviour"> | |||||
* <table> | |||||
* <caption>Sample behaviour</caption> | |||||
* <tr><th>Input Return</th><th>Value </th><th>Thrown Error</th></tr> | * <tr><th>Input Return</th><th>Value </th><th>Thrown Error</th></tr> | ||||
* <tr><td>5</td><td>4</td><td> </td></tr> | * <tr><td>5</td><td>4</td><td> </td></tr> | ||||
* <tr><td>2.9</td><td>2</td><td> </td></tr> | * <tr><td>2.9</td><td>2</td><td> </td></tr> |
import static org.apache.poi.ss.formula.functions.AggregateFunction.subtotalInstance; | import static org.apache.poi.ss.formula.functions.AggregateFunction.subtotalInstance; | ||||
import java.util.ArrayList; | |||||
import java.util.Arrays; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
import org.apache.poi.ss.formula.LazyRefEval; | import org.apache.poi.ss.formula.LazyRefEval; | ||||
import org.apache.poi.ss.formula.eval.ErrorEval; | import org.apache.poi.ss.formula.eval.ErrorEval; | ||||
import org.apache.poi.ss.formula.eval.EvaluationException; | import org.apache.poi.ss.formula.eval.EvaluationException; | ||||
import org.apache.poi.ss.formula.eval.NotImplementedException; | |||||
import org.apache.poi.ss.formula.eval.NotImplementedFunctionException; | import org.apache.poi.ss.formula.eval.NotImplementedFunctionException; | ||||
import org.apache.poi.ss.formula.eval.OperandResolver; | import org.apache.poi.ss.formula.eval.OperandResolver; | ||||
import org.apache.poi.ss.formula.eval.ValueEval; | import org.apache.poi.ss.formula.eval.ValueEval; | ||||
import java.util.ArrayList; | |||||
import java.util.Arrays; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
/** | /** | ||||
* Implementation for the Excel function SUBTOTAL<p> | * Implementation for the Excel function SUBTOTAL<p> | ||||
* | * | ||||
* <b>Syntax :</b> <br> | * <b>Syntax :</b> <br> | ||||
* SUBTOTAL ( <b>functionCode</b>, <b>ref1</b>, ref2 ... ) <br> | * SUBTOTAL ( <b>functionCode</b>, <b>ref1</b>, ref2 ... ) <br> | ||||
* <table border="1" cellpadding="1" cellspacing="0" summary="Parameter descriptions"> | |||||
* <table> | |||||
* <caption>Parameter descriptions</caption> | |||||
* <tr><td><b>functionCode</b></td><td>(1-11) Selects the underlying aggregate function to be used (see table below)</td></tr> | * <tr><td><b>functionCode</b></td><td>(1-11) Selects the underlying aggregate function to be used (see table below)</td></tr> | ||||
* <tr><td><b>ref1</b>, ref2 ...</td><td>Arguments to be passed to the underlying aggregate function</td></tr> | * <tr><td><b>ref1</b>, ref2 ...</td><td>Arguments to be passed to the underlying aggregate function</td></tr> | ||||
* </table><br> | * </table><br> | ||||
* </p> | |||||
* | * | ||||
* <table border="1" cellpadding="1" cellspacing="0" summary="Parameter descriptions"> | |||||
* <table> | |||||
* <caption>Parameter descriptions</caption> | |||||
* <tr><th>functionCode</th><th>Aggregate Function</th></tr> | * <tr><th>functionCode</th><th>Aggregate Function</th></tr> | ||||
* <tr align='center'><td>1</td><td>AVERAGE</td></tr> | |||||
* <tr align='center'><td>2</td><td>COUNT</td></tr> | |||||
* <tr align='center'><td>3</td><td>COUNTA</td></tr> | |||||
* <tr align='center'><td>4</td><td>MAX</td></tr> | |||||
* <tr align='center'><td>5</td><td>MIN</td></tr> | |||||
* <tr align='center'><td>6</td><td>PRODUCT</td></tr> | |||||
* <tr align='center'><td>7</td><td>STDEV</td></tr> | |||||
* <tr align='center'><td>8</td><td>STDEVP *</td></tr> | |||||
* <tr align='center'><td>9</td><td>SUM</td></tr> | |||||
* <tr align='center'><td>10</td><td>VAR *</td></tr> | |||||
* <tr align='center'><td>11</td><td>VARP *</td></tr> | |||||
* <tr align='center'><td>101</td><td>AVERAGE</td></tr> | |||||
* <tr align='center'><td>102</td><td>COUNT</td></tr> | |||||
* <tr align='center'><td>103</td><td>COUNTA</td></tr> | |||||
* <tr align='center'><td>104</td><td>MAX</td></tr> | |||||
* <tr align='center'><td>105</td><td>MIN</td></tr> | |||||
* <tr align='center'><td>106</td><td>PRODUCT</td></tr> | |||||
* <tr align='center'><td>107</td><td>STDEV</td></tr> | |||||
* <tr align='center'><td>108</td><td>STDEVP *</td></tr> | |||||
* <tr align='center'><td>109</td><td>SUM</td></tr> | |||||
* <tr align='center'><td>110</td><td>VAR *</td></tr> | |||||
* <tr align='center'><td>111</td><td>VARP *</td></tr> | |||||
* <tr><td>1</td><td>AVERAGE</td></tr> | |||||
* <tr><td>2</td><td>COUNT</td></tr> | |||||
* <tr><td>3</td><td>COUNTA</td></tr> | |||||
* <tr><td>4</td><td>MAX</td></tr> | |||||
* <tr><td>5</td><td>MIN</td></tr> | |||||
* <tr><td>6</td><td>PRODUCT</td></tr> | |||||
* <tr><td>7</td><td>STDEV</td></tr> | |||||
* <tr><td>8</td><td>STDEVP *</td></tr> | |||||
* <tr><td>9</td><td>SUM</td></tr> | |||||
* <tr><td>10</td><td>VAR *</td></tr> | |||||
* <tr><td>11</td><td>VARP *</td></tr> | |||||
* <tr><td>101</td><td>AVERAGE</td></tr> | |||||
* <tr><td>102</td><td>COUNT</td></tr> | |||||
* <tr><td>103</td><td>COUNTA</td></tr> | |||||
* <tr><td>104</td><td>MAX</td></tr> | |||||
* <tr><td>105</td><td>MIN</td></tr> | |||||
* <tr><td>106</td><td>PRODUCT</td></tr> | |||||
* <tr><td>107</td><td>STDEV</td></tr> | |||||
* <tr><td>108</td><td>STDEVP *</td></tr> | |||||
* <tr><td>109</td><td>SUM</td></tr> | |||||
* <tr><td>110</td><td>VAR *</td></tr> | |||||
* <tr><td>111</td><td>VARP *</td></tr> | |||||
* </table><br> | * </table><br> | ||||
* * Not implemented in POI yet. Functions 101-111 are the same as functions 1-11 but with | * * Not implemented in POI yet. Functions 101-111 are the same as functions 1-11 but with | ||||
* the option 'ignore hidden values'. | * the option 'ignore hidden values'. | ||||
* <p> | |||||
*/ | */ | ||||
public class Subtotal implements Function { | public class Subtotal implements Function { | ||||
throw EvaluationException.invalidValue(); | throw EvaluationException.invalidValue(); | ||||
} | } | ||||
@Override | |||||
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { | public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { | ||||
int nInnerArgs = args.length-1; // -1: first arg is used to select from a basic aggregate function | int nInnerArgs = args.length-1; // -1: first arg is used to select from a basic aggregate function | ||||
if (nInnerArgs < 1) { | if (nInnerArgs < 1) { | ||||
} | } | ||||
final Function innerFunc; | final Function innerFunc; | ||||
int functionCode = 0; | |||||
int functionCode; | |||||
try { | try { | ||||
ValueEval ve = OperandResolver.getSingleValue(args[0], srcRowIndex, srcColumnIndex); | ValueEval ve = OperandResolver.getSingleValue(args[0], srcRowIndex, srcColumnIndex); | ||||
functionCode = OperandResolver.coerceValueToInt(ve); | functionCode = OperandResolver.coerceValueToInt(ve); |
* | * | ||||
* Syntax : <br> | * Syntax : <br> | ||||
* SUMIF ( <b>range</b>, <b>criteria</b>, sum_range ) <br> | * SUMIF ( <b>range</b>, <b>criteria</b>, sum_range ) <br> | ||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions"> | |||||
* <table> | |||||
* <caption>Parameter descriptions</caption> | |||||
* <tr><th>range</th><td>The range over which criteria is applied. Also used for addend values when the third parameter is not present</td></tr> | * <tr><th>range</th><td>The range over which criteria is applied. Also used for addend values when the third parameter is not present</td></tr> | ||||
* <tr><th>criteria</th><td>The value or expression used to filter rows from <b>range</b></td></tr> | * <tr><th>criteria</th><td>The value or expression used to filter rows from <b>range</b></td></tr> | ||||
* <tr><th>sum_range</th><td>Locates the top-left corner of the corresponding range of addends - values to be added (after being selected by the criteria)</td></tr> | * <tr><th>sum_range</th><td>Locates the top-left corner of the corresponding range of addends - values to be added (after being selected by the criteria)</td></tr> | ||||
*/ | */ | ||||
public final class Sumif extends Var2or3ArgFunction { | public final class Sumif extends Var2or3ArgFunction { | ||||
@Override | |||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { | public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { | ||||
AreaEval aeRange; | AreaEval aeRange; | ||||
return eval(srcRowIndex, srcColumnIndex, arg1, aeRange, aeRange); | return eval(srcRowIndex, srcColumnIndex, arg1, aeRange, aeRange); | ||||
} | } | ||||
@Override | |||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, | public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, | ||||
ValueEval arg2) { | ValueEval arg2) { | ||||
* | * | ||||
* Syntax : <br> | * Syntax : <br> | ||||
* SUMPRODUCT ( array1[, array2[, array3[, ...]]]) | * SUMPRODUCT ( array1[, array2[, array3[, ...]]]) | ||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions"> | |||||
* <table> | |||||
* <caption>Parameter descriptions</caption> | |||||
* <tr><th>array1, ... arrayN </th><td>typically area references, | * <tr><th>array1, ... arrayN </th><td>typically area references, | ||||
* possibly cell references or scalar values</td></tr> | * possibly cell references or scalar values</td></tr> | ||||
* </table><br> | * </table><br> | ||||
public final class Sumproduct implements Function { | public final class Sumproduct implements Function { | ||||
@Override | |||||
public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { | public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { | ||||
int maxN = args.length; | int maxN = args.length; | ||||
int maxN = evalArgs.length; | int maxN = evalArgs.length; | ||||
double term = 1D; | double term = 1D; | ||||
for(int n=0; n<maxN; n++) { | |||||
double val = getScalarValue(evalArgs[n]); | |||||
for (ValueEval evalArg : evalArgs) { | |||||
double val = getScalarValue(evalArg); | |||||
term *= val; | term *= val; | ||||
} | } | ||||
return new NumberEval(term); | return new NumberEval(term); | ||||
} | } | ||||
private static boolean areasAllSameSize(TwoDEval[] args, int height, int width) { | private static boolean areasAllSameSize(TwoDEval[] args, int height, int width) { | ||||
for (int i = 0; i < args.length; i++) { | |||||
TwoDEval areaEval = args[i]; | |||||
for (TwoDEval areaEval : args) { | |||||
// check that height and width match | // check that height and width match | ||||
if(areaEval.getHeight() != height) { | |||||
if (areaEval.getHeight() != height) { | |||||
return false; | return false; | ||||
} | } | ||||
if(areaEval.getWidth() != width) { | |||||
if (areaEval.getWidth() != width) { | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Determines a <code>double</code> value for the specified <code>ValueEval</code>. | |||||
* @param isScalarProduct <code>false</code> for SUMPRODUCTs over area refs. | |||||
* @throws EvaluationException if <code>ve</code> represents an error value. | |||||
* Determines a {@code double} value for the specified {@code ValueEval}. | |||||
* @param isScalarProduct {@code false} for SUMPRODUCTs over area refs. | |||||
* @throws EvaluationException if {@code ve} represents an error value. | |||||
* <p> | * <p> | ||||
* Note - string values and empty cells are interpreted differently depending on | * Note - string values and empty cells are interpreted differently depending on | ||||
* <code>isScalarProduct</code>. For scalar products, if any term is blank or a string, the | |||||
* {@code isScalarProduct}. For scalar products, if any term is blank or a string, the | |||||
* error (#VALUE!) is raised. For area (sum)products, if any term is blank or a string, the | * error (#VALUE!) is raised. For area (sum)products, if any term is blank or a string, the | ||||
* result is zero. | * result is zero. | ||||
*/ | */ |
* | * | ||||
* Syntax:<br> | * Syntax:<br> | ||||
* TREND(known_y's, known_x's, new_x's, constant) | * TREND(known_y's, known_x's, new_x's, constant) | ||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions"> | |||||
* <table> | |||||
* <caption>Parameter descriptions</caption> | |||||
* <tr><th>known_y's, known_x's, new_x's</th><td>typically area references, possibly cell references or scalar values</td></tr> | * <tr><th>known_y's, known_x's, new_x's</th><td>typically area references, possibly cell references or scalar values</td></tr> | ||||
* <tr><th>constant</th><td><b>TRUE</b> or <b>FALSE</b>: | * <tr><th>constant</th><td><b>TRUE</b> or <b>FALSE</b>: | ||||
* determines whether the regression line should include an intercept term</td></tr> | * determines whether the regression line should include an intercept term</td></tr> | ||||
* of the same size as <b>known_y's</b>.<br> | * of the same size as <b>known_y's</b>.<br> | ||||
* If <b>new_x's</b> is not given, it is assumed to be the same as <b>known_x's</b><br> | * If <b>new_x's</b> is not given, it is assumed to be the same as <b>known_x's</b><br> | ||||
* If <b>constant</b> is omitted, it is assumed to be <b>TRUE</b> | * If <b>constant</b> is omitted, it is assumed to be <b>TRUE</b> | ||||
* </p> | |||||
*/ | */ | ||||
public final class Trend implements Function { | public final class Trend implements Function { | ||||
} | } | ||||
} | } | ||||
@Override | |||||
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { | public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { | ||||
if (args.length < 1 || args.length > 4) { | if (args.length < 1 || args.length > 4) { | ||||
return ErrorEval.VALUE_INVALID; | return ErrorEval.VALUE_INVALID; |
* POI's SpreadsheetAPI silently truncates the input argument to 31 characters. | * POI's SpreadsheetAPI silently truncates the input argument to 31 characters. | ||||
* Example: | * Example: | ||||
* | * | ||||
* <pre><code> | |||||
* <pre>{@code | |||||
* Sheet sheet = workbook.createSheet("My very long sheet name which is longer than 31 chars"); // will be truncated | * Sheet sheet = workbook.createSheet("My very long sheet name which is longer than 31 chars"); // will be truncated | ||||
* assert 31 == sheet.getSheetName().length(); | * assert 31 == sheet.getSheetName().length(); | ||||
* assert "My very long sheet name which i" == sheet.getSheetName(); | * assert "My very long sheet name which i" == sheet.getSheetName(); | ||||
* </code></pre> | |||||
* </p> | |||||
* }</pre> | |||||
* | * | ||||
* Except the 31-character constraint, Excel applies some other rules: | * Except the 31-character constraint, Excel applies some other rules: | ||||
* <p> | * <p> | ||||
* <li> closing square bracket (]) </li> | * <li> closing square bracket (]) </li> | ||||
* </ul> | * </ul> | ||||
* The string MUST NOT begin or end with the single quote (') character. | * The string MUST NOT begin or end with the single quote (') character. | ||||
* </p> | |||||
* | * | ||||
* <p> | * <p> | ||||
* See {@link org.apache.poi.ss.util.WorkbookUtil#createSafeSheetName(String nameProposal)} | * See {@link org.apache.poi.ss.util.WorkbookUtil#createSafeSheetName(String nameProposal)} | ||||
* Get sheet with the given name | * Get sheet with the given name | ||||
* | * | ||||
* @param name of the sheet | * @param name of the sheet | ||||
* @return Sheet with the name provided or <code>null</code> if it does not exist | |||||
* @return Sheet with the name provided or {@code null} if it does not exist | |||||
*/ | */ | ||||
Sheet getSheet(String name); | Sheet getSheet(String name); | ||||
/** | /** | ||||
* Finds a font that matches the one with the supplied attributes | * Finds a font that matches the one with the supplied attributes | ||||
* | * | ||||
* @return the font with the matched attributes or <code>null</code> | |||||
* @return the font with the matched attributes or {@code null} | |||||
*/ | */ | ||||
Font findFont(boolean bold, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline); | Font findFont(boolean bold, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline); | ||||
/** | /** | ||||
* @param name the name of the defined name | * @param name the name of the defined name | ||||
* @return the defined name with the specified name. <code>null</code> if not found. | |||||
* @return the defined name with the specified name. {@code null} if not found. | |||||
*/ | */ | ||||
Name getName(String name); | Name getName(String name); | ||||
CreationHelper getCreationHelper(); | CreationHelper getCreationHelper(); | ||||
/** | /** | ||||
* @return <code>false</code> if this workbook is not visible in the GUI | |||||
* @return {@code false} if this workbook is not visible in the GUI | |||||
*/ | */ | ||||
boolean isHidden(); | boolean isHidden(); | ||||
/** | /** | ||||
* @param hiddenFlag pass <code>false</code> to make the workbook visible in the GUI | |||||
* @param hiddenFlag pass {@code false} to make the workbook visible in the GUI | |||||
*/ | */ | ||||
void setHidden(boolean hiddenFlag); | void setHidden(boolean hiddenFlag); | ||||
* ({@link #isSheetVeryHidden(int)}) | * ({@link #isSheetVeryHidden(int)}) | ||||
* </p> | * </p> | ||||
* @param sheetIx Number | * @param sheetIx Number | ||||
* @return <code>true</code> if sheet is hidden | |||||
* @return {@code true} if sheet is hidden | |||||
* @see #getSheetVisibility(int) | * @see #getSheetVisibility(int) | ||||
*/ | */ | ||||
boolean isSheetHidden(int sheetIx); | boolean isSheetHidden(int sheetIx); | ||||
* ({@link #isSheetHidden(int)}) | * ({@link #isSheetHidden(int)}) | ||||
* </p> | * </p> | ||||
* @param sheetIx sheet index to check | * @param sheetIx sheet index to check | ||||
* @return <code>true</code> if sheet is very hidden | |||||
* @return {@code true} if sheet is very hidden | |||||
* @see #getSheetVisibility(int) | * @see #getSheetVisibility(int) | ||||
*/ | */ | ||||
boolean isSheetVeryHidden(int sheetIx); | boolean isSheetVeryHidden(int sheetIx); |
* Returns a text representation of this area reference. | * Returns a text representation of this area reference. | ||||
* <p> | * <p> | ||||
* Example return values: | * Example return values: | ||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Example return values"> | |||||
* <table> | |||||
* <caption>Example return values</caption> | |||||
* <tr><th align='left'>Result</th><th align='left'>Comment</th></tr> | * <tr><th align='left'>Result</th><th align='left'>Comment</th></tr> | ||||
* <tr><td>A1:A1</td><td>Single cell area reference without sheet</td></tr> | * <tr><td>A1:A1</td><td>Single cell area reference without sheet</td></tr> | ||||
* <tr><td>A1:$C$1</td><td>Multi-cell area reference without sheet</td></tr> | * <tr><td>A1:$C$1</td><td>Multi-cell area reference without sheet</td></tr> |
* reference is valid (in range) becomes important. | * reference is valid (in range) becomes important. | ||||
* <p> | * <p> | ||||
* Note - that the maximum sheet size varies across Excel versions: | * Note - that the maximum sheet size varies across Excel versions: | ||||
* <blockquote><table border="0" cellpadding="1" cellspacing="0" | |||||
* summary="Notable cases."> | |||||
* <table> | |||||
* <caption>Notable cases.</caption> | |||||
* <tr><th>Version </th><th>File Format </th> | * <tr><th>Version </th><th>File Format </th> | ||||
* <th>Last Column </th><th>Last Row</th></tr> | * <th>Last Column </th><th>Last Row</th></tr> | ||||
* <tr><td>97-2003</td><td>BIFF8</td><td>"IV" (2^8)</td><td>65536 (2^14)</td></tr> | * <tr><td>97-2003</td><td>BIFF8</td><td>"IV" (2^8)</td><td>65536 (2^14)</td></tr> | ||||
* <tr><td>2007</td><td>BIFF12</td><td>"XFD" (2^14)</td><td>1048576 (2^20)</td></tr> | * <tr><td>2007</td><td>BIFF12</td><td>"XFD" (2^14)</td><td>1048576 (2^20)</td></tr> | ||||
* </table></blockquote> | |||||
* </table> | |||||
* | |||||
* POI currently targets BIFF8 (Excel 97-2003), so the following behaviour can be observed for | * POI currently targets BIFF8 (Excel 97-2003), so the following behaviour can be observed for | ||||
* this method: | * this method: | ||||
* <blockquote><table border="0" cellpadding="1" cellspacing="0" | * <blockquote><table border="0" cellpadding="1" cellspacing="0" | ||||
* Returns a text representation of this cell reference. | * Returns a text representation of this cell reference. | ||||
* <p> | * <p> | ||||
* Example return values: | * Example return values: | ||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Example return values"> | |||||
* <table> | |||||
* <caption>Example return values</caption> | |||||
* <tr><th align='left'>Result</th><th align='left'>Comment</th></tr> | * <tr><th align='left'>Result</th><th align='left'>Comment</th></tr> | ||||
* <tr><td>A1</td><td>Cell reference without sheet</td></tr> | * <tr><td>A1</td><td>Cell reference without sheet</td></tr> | ||||
* <tr><td>Sheet1!A1</td><td>Standard sheet name</td></tr> | * <tr><td>Sheet1!A1</td><td>Standard sheet name</td></tr> |
* </ul> | * </ul> | ||||
* IEEE 64-bit Double Rendering Comparison | * IEEE 64-bit Double Rendering Comparison | ||||
* | * | ||||
* <table border="1" cellpadding="2" cellspacing="0" summary="IEEE 64-bit Double Rendering Comparison"> | |||||
* <table> | |||||
* <caption>IEEE 64-bit Double Rendering Comparison</caption> | |||||
* <tr><th>Raw bits</th><th>Java</th><th>Excel</th></tr> | * <tr><th>Raw bits</th><th>Java</th><th>Excel</th></tr> | ||||
* | * | ||||
* <tr><td>0x0000000000000000L</td><td>0.0</td><td>0</td></tr> | * <tr><td>0x0000000000000000L</td><td>0.0</td><td>0</td></tr> |
* from top left cell and ends at bottom right cell. Here is a | * from top left cell and ends at bottom right cell. Here is a | ||||
* brief example (number in cell is it's ordinal number): | * brief example (number in cell is it's ordinal number): | ||||
* | * | ||||
* <table border="1"> | |||||
* <table> | |||||
* <caption>ordinal number example</caption> | |||||
* <tbody> | * <tbody> | ||||
* <tr><td>1</td><td>2</td></tr> | * <tr><td>1</td><td>2</td></tr> | ||||
* <tr><td>3</td><td>4</td></tr> | * <tr><td>3</td><td>4</td></tr> |
* removed in the n+2 release, a later version should be specified by this | * removed in the n+2 release, a later version should be specified by this | ||||
* annotation. The annotation version number should not include beta</p> | * annotation. The annotation version number should not include beta</p> | ||||
* | * | ||||
* <p>For example, a feature with a <code>@deprecated POI 3.15 beta 3</code> | |||||
* <p>For example, a feature with a {@code @deprecated POI 3.15 beta 3} | |||||
* is deprecated in POI 3.15 and 3.16 and becomes eligible for deletion during | * is deprecated in POI 3.15 and 3.16 and becomes eligible for deletion during | ||||
* the POI 3.17 release series, and may be deleted immediately after POI 3.16 is | * the POI 3.17 release series, and may be deleted immediately after POI 3.16 is | ||||
* released. This would be annotated <code>@Removal(version="3.17")</p>. | |||||
* | |||||
* released. This would be annotated {@code @Removal(version="3.17")}</p>. | |||||
* | |||||
* @since POI-3.15 beta 3 | * @since POI-3.15 beta 3 | ||||
*/ | */ | ||||
@Documented | @Documented | ||||
public @interface Removal { | public @interface Removal { | ||||
/** | /** | ||||
* The POI version when this feature may be removed. | * The POI version when this feature may be removed. | ||||
* | |||||
* | |||||
* To ensure that the version number can be compared to the current version | * To ensure that the version number can be compared to the current version | ||||
* and a unit test can generate a warning if a removal-eligible feature has | * and a unit test can generate a warning if a removal-eligible feature has | ||||
* not been removed yet, the version number should adhere to the following format: | * not been removed yet, the version number should adhere to the following format: | ||||
* <pre>{@code | |||||
* Format: "(?<major>\d+)\.(?<minor>\d+)" | * Format: "(?<major>\d+)\.(?<minor>\d+)" | ||||
* Example: "3.15" | * Example: "3.15" | ||||
* }</pre> | |||||
*/ | */ | ||||
String version() default ""; | String version() default ""; | ||||
// TODO: Verify that the version syntax is valid by parsing with a version-aware parser like | // TODO: Verify that the version syntax is valid by parsing with a version-aware parser like |