added names of known but uniterpreted BIFF records
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@689559 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
32388d3d2d
commit
afad417d66
@ -43,6 +43,7 @@ import org.apache.poi.hssf.record.SCLRecord;
|
|||||||
import org.apache.poi.hssf.record.SaveRecalcRecord;
|
import org.apache.poi.hssf.record.SaveRecalcRecord;
|
||||||
import org.apache.poi.hssf.record.SelectionRecord;
|
import org.apache.poi.hssf.record.SelectionRecord;
|
||||||
import org.apache.poi.hssf.record.UncalcedRecord;
|
import org.apache.poi.hssf.record.UncalcedRecord;
|
||||||
|
import org.apache.poi.hssf.record.UnknownRecord;
|
||||||
import org.apache.poi.hssf.record.WindowTwoRecord;
|
import org.apache.poi.hssf.record.WindowTwoRecord;
|
||||||
import org.apache.poi.hssf.record.aggregates.ConditionalFormattingTable;
|
import org.apache.poi.hssf.record.aggregates.ConditionalFormattingTable;
|
||||||
import org.apache.poi.hssf.record.aggregates.DataValidityTable;
|
import org.apache.poi.hssf.record.aggregates.DataValidityTable;
|
||||||
@ -57,8 +58,6 @@ import org.apache.poi.hssf.record.aggregates.PageSettingsBlock;
|
|||||||
* @author Josh Micich
|
* @author Josh Micich
|
||||||
*/
|
*/
|
||||||
final class RecordOrderer {
|
final class RecordOrderer {
|
||||||
// TODO - add UninterpretedRecord as base class for many of these
|
|
||||||
// unimplemented sids
|
|
||||||
|
|
||||||
// TODO - simplify logic using a generalised record ordering
|
// TODO - simplify logic using a generalised record ordering
|
||||||
|
|
||||||
@ -126,7 +125,7 @@ final class RecordOrderer {
|
|||||||
case PrintGridlinesRecord.sid:
|
case PrintGridlinesRecord.sid:
|
||||||
case GridsetRecord.sid:
|
case GridsetRecord.sid:
|
||||||
case DefaultRowHeightRecord.sid:
|
case DefaultRowHeightRecord.sid:
|
||||||
case 0x0081: // SHEETPR
|
case UnknownRecord.SHEETPR_0081:
|
||||||
return true;
|
return true;
|
||||||
// next is the 'Worksheet Protection Block'
|
// next is the 'Worksheet Protection Block'
|
||||||
}
|
}
|
||||||
@ -149,10 +148,10 @@ final class RecordOrderer {
|
|||||||
case SCLRecord.sid:
|
case SCLRecord.sid:
|
||||||
case PaneRecord.sid:
|
case PaneRecord.sid:
|
||||||
case SelectionRecord.sid:
|
case SelectionRecord.sid:
|
||||||
case 0x0099:// STANDARDWIDTH
|
case UnknownRecord.STANDARDWIDTH_0099:
|
||||||
// MergedCellsTable usually here
|
// MergedCellsTable usually here
|
||||||
case 0x015f:// LABELRANGES
|
case UnknownRecord.LABELRANGES_015F:
|
||||||
case 0x00ef:// PHONETICPR
|
case UnknownRecord.PHONETICPR_00EF:
|
||||||
return i + 1;
|
return i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,7 +167,7 @@ final class RecordOrderer {
|
|||||||
case SCLRecord.sid:
|
case SCLRecord.sid:
|
||||||
case PaneRecord.sid:
|
case PaneRecord.sid:
|
||||||
case SelectionRecord.sid:
|
case SelectionRecord.sid:
|
||||||
case 0x0099:// STANDARDWIDTH
|
case UnknownRecord.STANDARDWIDTH_0099:
|
||||||
return i + 1;
|
return i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,16 +228,16 @@ final class RecordOrderer {
|
|||||||
short sid = ((Record)rb).getSid();
|
short sid = ((Record)rb).getSid();
|
||||||
switch(sid) {
|
switch(sid) {
|
||||||
case WindowTwoRecord.sid:
|
case WindowTwoRecord.sid:
|
||||||
case 0x00A0: // SCL
|
case UnknownRecord.SCL_00A0:
|
||||||
case PaneRecord.sid:
|
case PaneRecord.sid:
|
||||||
case SelectionRecord.sid:
|
case SelectionRecord.sid:
|
||||||
case 0x0099: // STANDARDWIDTH
|
case UnknownRecord.STANDARDWIDTH_0099:
|
||||||
// MergedCellsTable
|
// MergedCellsTable
|
||||||
case 0x015F: // LABELRANGES
|
case UnknownRecord.LABELRANGES_015F:
|
||||||
case 0x00EF: // PHONETICPR
|
case UnknownRecord.PHONETICPR_00EF:
|
||||||
// ConditionalFormattingTable
|
// ConditionalFormattingTable
|
||||||
case HyperlinkRecord.sid:
|
case HyperlinkRecord.sid:
|
||||||
case 0x0800: // QUICKTIP
|
case UnknownRecord.QUICKTIP_0800:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -246,9 +245,9 @@ final class RecordOrderer {
|
|||||||
|
|
||||||
private static boolean isDVTSubsequentRecord(short sid) {
|
private static boolean isDVTSubsequentRecord(short sid) {
|
||||||
switch(sid) {
|
switch(sid) {
|
||||||
case 0x0862: // SHEETLAYOUT
|
case UnknownRecord.SHEETEXT_0862:
|
||||||
case 0x0867: // SHEETPROTECTION
|
case UnknownRecord.SHEETPROTECTION_0867:
|
||||||
case 0x0868: // RANGEPROTECTION
|
case UnknownRecord.RANGEPROTECTION_0868:
|
||||||
case EOFRecord.sid:
|
case EOFRecord.sid:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -16,9 +15,9 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.hssf.record;
|
package org.apache.poi.hssf.record;
|
||||||
|
|
||||||
|
import org.apache.poi.util.HexDump;
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,125 +30,259 @@ import org.apache.poi.util.LittleEndian;
|
|||||||
* @author Jason Height (jheight at chariot dot net dot au)
|
* @author Jason Height (jheight at chariot dot net dot au)
|
||||||
* @author Glen Stampoultzis (glens at apache.org)
|
* @author Glen Stampoultzis (glens at apache.org)
|
||||||
*/
|
*/
|
||||||
|
public final class UnknownRecord extends Record {
|
||||||
|
|
||||||
public class UnknownRecord
|
/*
|
||||||
extends Record
|
* Some Record IDs used by POI as 'milestones' in the record stream
|
||||||
{
|
*/
|
||||||
private short sid = 0;
|
public static final int PLS_004D = 0x004D;
|
||||||
private byte[] thedata = null;
|
public static final int SHEETPR_0081 = 0x0081;
|
||||||
|
public static final int STANDARDWIDTH_0099 = 0x0099;
|
||||||
|
public static final int SCL_00A0 = 0x00A0;
|
||||||
|
public static final int BITMAP_00E9 = 0x00E9;
|
||||||
|
public static final int PHONETICPR_00EF = 0x00EF;
|
||||||
|
public static final int LABELRANGES_015F = 0x015F;
|
||||||
|
public static final int QUICKTIP_0800 = 0x0800;
|
||||||
|
public static final int SHEETEXT_0862 = 0x0862; // OOO calls this SHEETLAYOUT
|
||||||
|
public static final int SHEETPROTECTION_0867 = 0x0867;
|
||||||
|
public static final int RANGEPROTECTION_0868 = 0x0868;
|
||||||
|
|
||||||
public UnknownRecord()
|
private int _sid;
|
||||||
{
|
private byte[] _rawData;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param id id of the record -not validated, just stored for serialization
|
* @param id id of the record -not validated, just stored for serialization
|
||||||
* @param data the data
|
* @param data the data
|
||||||
*/
|
*/
|
||||||
public UnknownRecord(short id, byte[] data)
|
public UnknownRecord(int id, byte[] data) {
|
||||||
{
|
_sid = id & 0xFFFF;
|
||||||
this.sid = id;
|
_rawData = data;
|
||||||
this.thedata = data;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* construct an unknown record. No fields are interperated and the record will
|
* construct an unknown record. No fields are interpreted and the record will
|
||||||
* be serialized in its original form more or less
|
* be serialized in its original form more or less
|
||||||
* @param in the RecordInputstream to read the record from
|
* @param in the RecordInputstream to read the record from
|
||||||
*/
|
*/
|
||||||
|
public UnknownRecord(RecordInputStream in) {
|
||||||
|
_sid = in.getSid();
|
||||||
|
_rawData = in.readRemainder();
|
||||||
|
if (false && getBiffName(_sid) == null) {
|
||||||
|
// unknown sids in the range 0x0004-0x0013 are probably 'sub-records' of ObjectRecord
|
||||||
|
// those sids are in a different number space.
|
||||||
|
// TODO - put unknown OBJ sub-records in a different class
|
||||||
|
System.out.println("Unknown record 0x" + Integer.toHexString(_sid).toUpperCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public UnknownRecord(RecordInputStream in)
|
/**
|
||||||
{
|
* spit the record out AS IS. no interpretation or identification
|
||||||
sid = in.getSid();
|
*/
|
||||||
thedata = in.readRemainder();
|
public final int serialize(int offset, byte[] data) {
|
||||||
|
LittleEndian.putUShort(data, 0 + offset, _sid);
|
||||||
|
int dataSize = _rawData.length;
|
||||||
|
LittleEndian.putUShort(data, 2 + offset, dataSize);
|
||||||
|
System.arraycopy(_rawData, 0, data, 4 + offset, dataSize);
|
||||||
|
return 4 + dataSize;
|
||||||
|
}
|
||||||
|
|
||||||
//System.out.println("UnknownRecord: 0x"+Integer.toHexString(sid));
|
public final int getRecordSize() {
|
||||||
}
|
return 4 + _rawData.length;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* spit the record out AS IS. no interpretation or identification
|
* NO OP!
|
||||||
*/
|
*/
|
||||||
public int serialize(int offset, byte [] data)
|
protected void validateSid(short id) {
|
||||||
{
|
// if we had a valid sid we wouldn't be using the "Unknown Record" record now would we?
|
||||||
if (thedata == null)
|
}
|
||||||
{
|
|
||||||
thedata = new byte[ 0 ];
|
|
||||||
}
|
|
||||||
LittleEndian.putShort(data, 0 + offset, sid);
|
|
||||||
LittleEndian.putShort(data, 2 + offset, ( short ) (thedata.length));
|
|
||||||
if (thedata.length > 0)
|
|
||||||
{
|
|
||||||
System.arraycopy(thedata, 0, data, 4 + offset, thedata.length);
|
|
||||||
}
|
|
||||||
return getRecordSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRecordSize()
|
/**
|
||||||
{
|
* print a sort of string representation ([UNKNOWN RECORD] id = x [/UNKNOWN RECORD])
|
||||||
int retval = 4;
|
*/
|
||||||
|
public final String toString() {
|
||||||
|
String biffName = getBiffName(_sid);
|
||||||
|
if (biffName == null) {
|
||||||
|
biffName = "UNKNOWNRECORD";
|
||||||
|
}
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
|
||||||
if (thedata != null)
|
sb.append("[").append(biffName).append("] (0x");
|
||||||
{
|
sb.append(Integer.toHexString(_sid).toUpperCase() + ")\n");
|
||||||
retval += thedata.length;
|
if (_rawData.length > 0) {
|
||||||
}
|
sb.append(" rawData=").append(HexDump.toHex(_rawData)).append("\n");
|
||||||
return retval;
|
}
|
||||||
}
|
sb.append("[/").append(biffName).append("]\n");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
protected void fillFields(byte [] data, short sid)
|
public final short getSid() {
|
||||||
{
|
return (short) _sid;
|
||||||
this.sid = sid;
|
}
|
||||||
thedata = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NO OP!
|
* These BIFF record types are known but still uninterpreted by POI
|
||||||
*/
|
*
|
||||||
|
* @return the documented name of this BIFF record type
|
||||||
|
*/
|
||||||
|
private static String getBiffName(int sid) {
|
||||||
|
// Note to POI developers:
|
||||||
|
// Make sure you delete the corresponding entry from
|
||||||
|
// this method any time a new Record subclass is created.
|
||||||
|
switch (sid) {
|
||||||
|
case PLS_004D: return "PLS";
|
||||||
|
case 0x0050: return "DCON";
|
||||||
|
case 0x007F: return "IMDATA";
|
||||||
|
case SHEETPR_0081: return "SHEETPR";
|
||||||
|
case 0x0090: return "SORT";
|
||||||
|
case 0x0094: return "LHRECORD";
|
||||||
|
case STANDARDWIDTH_0099: return "STANDARDWIDTH";
|
||||||
|
case 0x009D: return "AUTOFILTERINFO";
|
||||||
|
case SCL_00A0: return "SCL";
|
||||||
|
case 0x00AE: return "SCENMAN";
|
||||||
|
case 0x00D3: return "OBPROJ";
|
||||||
|
case 0x00DC: return "PARAMQRY";
|
||||||
|
case 0x00DE: return "OLESIZE";
|
||||||
|
case BITMAP_00E9: return "BITMAP";
|
||||||
|
case PHONETICPR_00EF: return "PHONETICPR";
|
||||||
|
|
||||||
protected void validateSid(short id)
|
case LABELRANGES_015F: return "LABELRANGES";
|
||||||
{
|
case 0x01BA: return "CODENAME";
|
||||||
|
case 0x01A9: return "USERBVIEW";
|
||||||
|
case 0x01AA: return "USERSVIEWBEGIN";
|
||||||
|
case 0x01AB: return "USERSVIEWEND";
|
||||||
|
case 0x01AD: return "QSI";
|
||||||
|
|
||||||
// if we had a valid sid we wouldn't be using the "Unknown Record" record now would we?
|
case 0x01C0: return "EXCEL9FILE";
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
case 0x0802: return "QSISXTAG";
|
||||||
* print a sort of string representation ([UNKNOWN RECORD] id = x [/UNKNOWN RECORD])
|
case 0x0803: return "DBQUERYEXT";
|
||||||
*/
|
case 0x0805: return "TXTQUERY";
|
||||||
|
|
||||||
public String toString()
|
case QUICKTIP_0800: return "QUICKTIP";
|
||||||
{
|
case 0x0850: return "CHARTFRTINFO";
|
||||||
StringBuffer buffer = new StringBuffer();
|
case 0x0852: return "STARTBLOCK";
|
||||||
|
case 0x0853: return "ENDBLOCK";
|
||||||
|
case 0x0856: return "CATLAB";
|
||||||
|
case SHEETEXT_0862: return "SHEETEXT";
|
||||||
|
case 0x0863: return "BOOKEXT";
|
||||||
|
case SHEETPROTECTION_0867: return "SHEETPROTECTION";
|
||||||
|
case RANGEPROTECTION_0868: return "RANGEPROTECTION";
|
||||||
|
case 0x086B: return "DATALABEXTCONTENTS";
|
||||||
|
case 0x086C: return "CELLWATCH";
|
||||||
|
case 0x0874: return "DROPDOWNOBJIDS";
|
||||||
|
case 0x0876: return "DCONN";
|
||||||
|
case 0x087B: return "CFEX";
|
||||||
|
case 0x087C: return "XFCRC";
|
||||||
|
case 0x087D: return "XFEXT";
|
||||||
|
case 0x088B: return "PLV";
|
||||||
|
case 0x088C: return "COMPAT12";
|
||||||
|
case 0x088D: return "DXF";
|
||||||
|
case 0x088E: return "TABLESTYLES";
|
||||||
|
case 0x0892: return "STYLEEXT";
|
||||||
|
case 0x0896: return "THEME";
|
||||||
|
case 0x0897: return "GUIDTYPELIB";
|
||||||
|
case 0x089A: return "MTRSETTINGS";
|
||||||
|
case 0x089B: return "COMPRESSPICTURES";
|
||||||
|
case 0x089C: return "HEADERFOOTER";
|
||||||
|
case 0x08A3: return "FORCEFULLCALCULATION";
|
||||||
|
case 0x08A4: return "SHAPEPROPSSTREAM";
|
||||||
|
case 0x08A5: return "TEXTPROPSSTREAM";
|
||||||
|
case 0x08A6: return "RICHTEXTSTREAM";
|
||||||
|
|
||||||
buffer.append("[UNKNOWN RECORD:" + Integer.toHexString(sid) + "]\n");
|
case 0x08C8: return "PLV{Mac Excel}";
|
||||||
buffer.append(" .id = ").append(Integer.toHexString(sid))
|
|
||||||
.append("\n");
|
|
||||||
buffer.append("[/UNKNOWN RECORD]\n");
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public short getSid()
|
case 0x1051: return "SHAPEPROPSSTREAM";
|
||||||
{
|
|
||||||
return sid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
}
|
||||||
* called by the constructor, should set class level fields. Should throw
|
if (isObservedButUnknown(sid)) {
|
||||||
* runtime exception for bad/icomplete data.
|
return "UNKNOWN-" + Integer.toHexString(sid).toUpperCase();
|
||||||
*
|
}
|
||||||
* @param in the RecordInputstream to read the record from
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected void fillFields(RecordInputStream in)
|
return null;
|
||||||
{
|
}
|
||||||
throw new RecordFormatException(
|
|
||||||
"Unknown record cannot be constructed via offset -- we need a copy of the data");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Unlike the other Record.clone methods this is a shallow clone*/
|
/**
|
||||||
public Object clone() {
|
*
|
||||||
UnknownRecord rec = new UnknownRecord();
|
* @return <code>true</code> if the unknown record id has been observed in POI unit tests
|
||||||
rec.sid = sid;
|
*/
|
||||||
rec.thedata = thedata;
|
private static boolean isObservedButUnknown(int sid) {
|
||||||
return rec;
|
switch (sid) {
|
||||||
}
|
case 0x0033:
|
||||||
|
// contains 2 bytes of data: 0x0001 or 0x0003
|
||||||
|
case 0x0034:
|
||||||
|
// Seems to be written by MSAccess
|
||||||
|
// contains text "[Microsoft JET Created Table]0021010"
|
||||||
|
// appears after last cell value record and before WINDOW2
|
||||||
|
case 0x01BD:
|
||||||
|
case 0x01C2:
|
||||||
|
// Written by Excel 2007
|
||||||
|
// rawData is multiple of 12 bytes long
|
||||||
|
// appears after last cell value record and before WINDOW2 or drawing records
|
||||||
|
case 0x089D:
|
||||||
|
case 0x089E:
|
||||||
|
case 0x08A7:
|
||||||
|
|
||||||
|
case 0x1001:
|
||||||
|
case 0x1006:
|
||||||
|
case 0x1007:
|
||||||
|
case 0x1009:
|
||||||
|
case 0x100A:
|
||||||
|
case 0x100B:
|
||||||
|
case 0x100C:
|
||||||
|
case 0x1014:
|
||||||
|
case 0x1017:
|
||||||
|
case 0x1018:
|
||||||
|
case 0x1019:
|
||||||
|
case 0x101A:
|
||||||
|
case 0x101B:
|
||||||
|
case 0x101D:
|
||||||
|
case 0x101E:
|
||||||
|
case 0x101F:
|
||||||
|
case 0x1020:
|
||||||
|
case 0x1021:
|
||||||
|
case 0x1022:
|
||||||
|
case 0x1024:
|
||||||
|
case 0x1025:
|
||||||
|
case 0x1026:
|
||||||
|
case 0x1027:
|
||||||
|
case 0x1032:
|
||||||
|
case 0x1033:
|
||||||
|
case 0x1034:
|
||||||
|
case 0x1035:
|
||||||
|
case 0x103A:
|
||||||
|
case 0x1041:
|
||||||
|
case 0x1043:
|
||||||
|
case 0x1044:
|
||||||
|
case 0x1045:
|
||||||
|
case 0x1046:
|
||||||
|
case 0x104A:
|
||||||
|
case 0x104B:
|
||||||
|
case 0x104E:
|
||||||
|
case 0x104F:
|
||||||
|
case 0x1051:
|
||||||
|
case 0x105C:
|
||||||
|
case 0x105D:
|
||||||
|
case 0x105F:
|
||||||
|
case 0x1060:
|
||||||
|
case 0x1062:
|
||||||
|
case 0x1063:
|
||||||
|
case 0x1064:
|
||||||
|
case 0x1065:
|
||||||
|
case 0x1066:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void fillFields(RecordInputStream in) {
|
||||||
|
throw new RecordFormatException(
|
||||||
|
"Unknown record cannot be constructed via offset -- we need a copy of the data");
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Object clone() {
|
||||||
|
// immutable - ok to return this
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ import org.apache.poi.hssf.record.PrintSetupRecord;
|
|||||||
import org.apache.poi.hssf.record.Record;
|
import org.apache.poi.hssf.record.Record;
|
||||||
import org.apache.poi.hssf.record.RightMarginRecord;
|
import org.apache.poi.hssf.record.RightMarginRecord;
|
||||||
import org.apache.poi.hssf.record.TopMarginRecord;
|
import org.apache.poi.hssf.record.TopMarginRecord;
|
||||||
|
import org.apache.poi.hssf.record.UnknownRecord;
|
||||||
import org.apache.poi.hssf.record.VCenterRecord;
|
import org.apache.poi.hssf.record.VCenterRecord;
|
||||||
import org.apache.poi.hssf.record.VerticalPageBreakRecord;
|
import org.apache.poi.hssf.record.VerticalPageBreakRecord;
|
||||||
|
|
||||||
@ -99,9 +100,9 @@ public final class PageSettingsBlock extends RecordAggregate {
|
|||||||
case RightMarginRecord.sid:
|
case RightMarginRecord.sid:
|
||||||
case TopMarginRecord.sid:
|
case TopMarginRecord.sid:
|
||||||
case BottomMarginRecord.sid:
|
case BottomMarginRecord.sid:
|
||||||
case 0x004D: // PLS
|
case UnknownRecord.PLS_004D:
|
||||||
case PrintSetupRecord.sid:
|
case PrintSetupRecord.sid:
|
||||||
case 0x00E9: // BITMAP
|
case UnknownRecord.BITMAP_00E9:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
Loading…
Reference in New Issue
Block a user