or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations-and-mapping.mddata-conversion.mdevent-system.mdexception-handling.mdindex.mdreading-operations.mdwriting-operations.md

event-system.mddocs/

0

# Event System

1

2

Event-driven processing system with listeners for handling large datasets efficiently. EasyExcel uses a listener pattern for memory-efficient processing of Excel files, allowing row-by-row processing without loading entire datasets into memory.

3

4

## Capabilities

5

6

### Core Listener Interface

7

8

Primary interface for handling read events during Excel processing.

9

10

```java { .api }

11

/**

12

* Main interface for handling read events

13

* @param <T> Type of data object for each row

14

*/

15

public interface ReadListener<T> extends Listener {

16

/**

17

* Process each row of data

18

* @param data Parsed data object for current row

19

* @param context Analysis context with metadata

20

*/

21

void invoke(T data, AnalysisContext context);

22

23

/**

24

* Called after all data has been analyzed

25

* @param context Analysis context with final metadata

26

*/

27

void doAfterAllAnalysed(AnalysisContext context);

28

29

/**

30

* Handle exceptions during reading

31

* @param exception Exception that occurred

32

* @param context Analysis context when exception occurred

33

* @throws Exception Can re-throw or handle the exception

34

*/

35

default void onException(Exception exception, AnalysisContext context) throws Exception {

36

throw exception;

37

}

38

39

/**

40

* Process header row data

41

* @param headMap Map of column index to header cell data

42

* @param context Analysis context

43

*/

44

default void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {

45

// Default implementation does nothing

46

}

47

48

/**

49

* Process extra information (comments, hyperlinks, merged regions)

50

* @param extra Extra cell information

51

* @param context Analysis context

52

*/

53

default void extra(CellExtra extra, AnalysisContext context) {

54

// Default implementation does nothing

55

}

56

57

/**

58

* Control whether to continue reading

59

* @param context Analysis context

60

* @return true to continue reading, false to stop

61

*/

62

default boolean hasNext(AnalysisContext context) {

63

return true;

64

}

65

}

66

```

67

68

### Analysis Context

69

70

Context interface providing metadata and state information during reading.

71

72

```java { .api }

73

/**

74

* Provides context information during read operations

75

*/

76

public interface AnalysisContext {

77

/**

78

* Get the current row's analysis result

79

* @return Current row data object

80

*/

81

Object getCurrentRowAnalysisResult();

82

83

/**

84

* Get current row number (0-based)

85

* @return Row number

86

*/

87

Integer getCurrentRowNum();

88

89

/**

90

* Get current sheet information

91

* @return ReadSheet configuration

92

*/

93

ReadSheet getCurrentSheet();

94

95

/**

96

* Get Excel file type

97

* @return ExcelTypeEnum (XLS, XLSX, CSV)

98

*/

99

ExcelTypeEnum getExcelType();

100

101

/**

102

* Get read holder with configuration

103

* @return ReadHolder with settings

104

*/

105

ReadHolder currentReadHolder();

106

107

/**

108

* Get global configuration

109

* @return GlobalConfiguration settings

110

*/

111

GlobalConfiguration getGlobalConfiguration();

112

113

/**

114

* Get current workbook holder

115

* @return ReadWorkbook configuration

116

*/

117

ReadWorkbookHolder getReadWorkbookHolder();

118

119

/**

120

* Get current sheet holder

121

* @return ReadSheetHolder configuration

122

*/

123

ReadSheetHolder getReadSheetHolder();

124

125

/**

126

* Interrupt the reading process

127

*/

128

void interrupt();

129

}

130

```

131

132

### Abstract Base Listener

133

134

Abstract base implementation providing common functionality.

135

136

```java { .api }

137

/**

138

* Abstract base implementation of ReadListener

139

* @param <T> Type of data object for each row

140

*/

141

public abstract class AnalysisEventListener<T> implements ReadListener<T> {

142

/**

143

* Process each row of data (must be implemented)

144

* @param data Parsed data object for current row

145

* @param context Analysis context with metadata

146

*/

147

public abstract void invoke(T data, AnalysisContext context);

148

149

/**

150

* Called after all data has been analyzed (must be implemented)

151

* @param context Analysis context with final metadata

152

*/

153

public abstract void doAfterAllAnalysed(AnalysisContext context);

154

155

/**

156

* Handle exceptions during reading

157

* @param exception Exception that occurred

158

* @param context Analysis context when exception occurred

159

*/

160

@Override

161

public void onException(Exception exception, AnalysisContext context) throws Exception {

162

throw exception;

163

}

164

165

/**

166

* Process header row data

167

* @param headMap Map of column index to header cell data

168

* @param context Analysis context

169

*/

170

@Override

171

public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {

172

// Default implementation does nothing

173

}

174

}

175

```

176

177

### Built-in Listener Implementations

178

179

Pre-built listeners for common scenarios.

180

181

```java { .api }

182

/**

183

* Synchronous listener that collects all data in a List

184

* @param <T> Type of data object

185

*/

186

public class SyncReadListener<T> implements ReadListener<T> {

187

private final List<T> list = new ArrayList<>();

188

189

@Override

190

public void invoke(T data, AnalysisContext context) {

191

list.add(data);

192

}

193

194

@Override

195

public void doAfterAllAnalysed(AnalysisContext context) {

196

// Analysis completed

197

}

198

199

/**

200

* Get all collected data

201

* @return List of all data objects

202

*/

203

public List<T> getList() {

204

return list;

205

}

206

}

207

208

/**

209

* Paginated listener for processing large datasets in batches

210

* @param <T> Type of data object

211

*/

212

public abstract class PageReadListener<T> extends AnalysisEventListener<T> {

213

private final int batchCount;

214

private List<T> cachedDataList = new ArrayList<>();

215

216

/**

217

* Create paginated listener with batch size

218

* @param batchCount Number of records per batch

219

*/

220

public PageReadListener(int batchCount) {

221

this.batchCount = batchCount;

222

}

223

224

@Override

225

public void invoke(T data, AnalysisContext context) {

226

cachedDataList.add(data);

227

if (cachedDataList.size() >= batchCount) {

228

invokeAfterBatch(cachedDataList, context);

229

cachedDataList = new ArrayList<>();

230

}

231

}

232

233

@Override

234

public void doAfterAllAnalysed(AnalysisContext context) {

235

if (!cachedDataList.isEmpty()) {

236

invokeAfterBatch(cachedDataList, context);

237

}

238

doAfterAllPageRead(context);

239

}

240

241

/**

242

* Process a batch of data

243

* @param dataList Batch of data objects

244

* @param context Analysis context

245

*/

246

public abstract void invokeAfterBatch(List<T> dataList, AnalysisContext context);

247

248

/**

249

* Called after all pages have been processed

250

* @param context Analysis context

251

*/

252

public void doAfterAllPageRead(AnalysisContext context) {

253

// Default implementation does nothing

254

}

255

}

256

257

/**

258

* Listener that ignores exceptions and continues reading

259

* @param <T> Type of data object

260

*/

261

public class IgnoreExceptionReadListener<T> implements ReadListener<T> {

262

private final ReadListener<T> delegate;

263

264

/**

265

* Create listener that wraps another listener and ignores exceptions

266

* @param delegate Listener to delegate to

267

*/

268

public IgnoreExceptionReadListener(ReadListener<T> delegate) {

269

this.delegate = delegate;

270

}

271

272

@Override

273

public void invoke(T data, AnalysisContext context) {

274

try {

275

delegate.invoke(data, context);

276

} catch (Exception e) {

277

// Log and ignore the exception

278

System.err.println("Exception ignored: " + e.getMessage());

279

}

280

}

281

282

@Override

283

public void doAfterAllAnalysed(AnalysisContext context) {

284

delegate.doAfterAllAnalysed(context);

285

}

286

287

@Override

288

public void onException(Exception exception, AnalysisContext context) {

289

// Ignore exceptions

290

System.err.println("Exception ignored: " + exception.getMessage());

291

}

292

}

293

```

294

295

### Extra Information Classes

296

297

Classes for handling additional cell information.

298

299

```java { .api }

300

/**

301

* Extra cell information (comments, hyperlinks, merged regions)

302

*/

303

public class CellExtra {

304

/**

305

* Type of extra information

306

*/

307

private CellExtraTypeEnum type;

308

309

/**

310

* Text content (for comments and hyperlinks)

311

*/

312

private String text;

313

314

/**

315

* First row index (0-based)

316

*/

317

private Integer firstRowIndex;

318

319

/**

320

* Last row index (0-based)

321

*/

322

private Integer lastRowIndex;

323

324

/**

325

* First column index (0-based)

326

*/

327

private Integer firstColumnIndex;

328

329

/**

330

* Last column index (0-based)

331

*/

332

private Integer lastColumnIndex;

333

334

// Getters and setters...

335

}

336

337

/**

338

* Types of extra cell information

339

*/

340

public enum CellExtraTypeEnum {

341

/**

342

* Cell comments

343

*/

344

COMMENT,

345

346

/**

347

* Hyperlinks

348

*/

349

HYPERLINK,

350

351

/**

352

* Merged cell regions

353

*/

354

MERGE

355

}

356

```

357

358

### Supporting Interfaces

359

360

Base interfaces for the listener system.

361

362

```java { .api }

363

/**

364

* Base marker interface for all listeners

365

*/

366

public interface Listener {

367

}

368

369

/**

370

* Base marker interface for event handlers

371

*/

372

public interface Handler {

373

}

374

375

/**

376

* Interface for controlling execution order

377

*/

378

public interface Order {

379

/**

380

* Get execution order (lower values execute first)

381

* @return Order value

382

*/

383

int order();

384

}

385

```

386

387

## Usage Examples

388

389

### Basic Custom Listener

390

```java

391

import com.alibaba.excel.context.AnalysisContext;

392

import com.alibaba.excel.event.AnalysisEventListener;

393

394

public class UserDataListener extends AnalysisEventListener<UserData> {

395

private final List<UserData> users = new ArrayList<>();

396

397

@Override

398

public void invoke(UserData user, AnalysisContext context) {

399

// Validate data

400

if (user.getName() != null && user.getEmail() != null) {

401

users.add(user);

402

System.out.println("Processed user: " + user.getName());

403

} else {

404

System.err.println("Invalid user data at row: " + context.getCurrentRowNum());

405

}

406

}

407

408

@Override

409

public void doAfterAllAnalysed(AnalysisContext context) {

410

System.out.println("Analysis completed. Total valid users: " + users.size());

411

412

// Process all users (save to database, etc.)

413

saveUsers(users);

414

}

415

416

@Override

417

public void onException(Exception exception, AnalysisContext context) {

418

System.err.println("Error at row " + context.getCurrentRowNum() +

419

": " + exception.getMessage());

420

// Continue processing instead of stopping

421

}

422

423

private void saveUsers(List<UserData> users) {

424

// Implementation for saving users

425

}

426

}

427

428

// Usage

429

EasyExcel.read("users.xlsx", UserData.class, new UserDataListener())

430

.sheet()

431

.doRead();

432

```

433

434

### Paginated Processing for Large Files

435

```java

436

import com.alibaba.excel.read.listener.PageReadListener;

437

import com.alibaba.excel.context.AnalysisContext;

438

439

public class UserBatchProcessor extends PageReadListener<UserData> {

440

private final UserService userService;

441

442

public UserBatchProcessor(UserService userService) {

443

super(1000); // Process in batches of 1000

444

this.userService = userService;

445

}

446

447

@Override

448

public void invokeAfterBatch(List<UserData> userBatch, AnalysisContext context) {

449

System.out.println("Processing batch of " + userBatch.size() + " users");

450

451

// Process batch (validate, transform, save)

452

List<UserData> validUsers = userBatch.stream()

453

.filter(this::isValid)

454

.collect(Collectors.toList());

455

456

userService.saveBatch(validUsers);

457

458

System.out.println("Saved " + validUsers.size() + " valid users");

459

}

460

461

@Override

462

public void doAfterAllPageRead(AnalysisContext context) {

463

System.out.println("Completed processing all data");

464

userService.finalizeImport();

465

}

466

467

private boolean isValid(UserData user) {

468

return user.getName() != null &&

469

user.getEmail() != null &&

470

user.getEmail().contains("@");

471

}

472

}

473

474

// Usage

475

EasyExcel.read("large-users.xlsx", UserData.class, new UserBatchProcessor(userService))

476

.sheet()

477

.doRead();

478

```

479

480

### Handling Headers and Extra Information

481

```java

482

import com.alibaba.excel.context.AnalysisContext;

483

import com.alibaba.excel.event.AnalysisEventListener;

484

import com.alibaba.excel.metadata.CellExtra;

485

import com.alibaba.excel.metadata.data.ReadCellData;

486

487

public class DetailedDataListener extends AnalysisEventListener<UserData> {

488

private Map<Integer, String> headers = new HashMap<>();

489

private List<String> comments = new ArrayList<>();

490

491

@Override

492

public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {

493

System.out.println("Processing headers:");

494

headMap.forEach((index, cellData) -> {

495

String headerName = cellData.getStringValue();

496

headers.put(index, headerName);

497

System.out.println("Column " + index + ": " + headerName);

498

});

499

}

500

501

@Override

502

public void invoke(UserData data, AnalysisContext context) {

503

System.out.println("Row " + context.getCurrentRowNum() + ": " + data);

504

}

505

506

@Override

507

public void extra(CellExtra extra, AnalysisContext context) {

508

switch (extra.getType()) {

509

case COMMENT:

510

comments.add("Comment at (" + extra.getFirstRowIndex() +

511

"," + extra.getFirstColumnIndex() + "): " + extra.getText());

512

break;

513

case HYPERLINK:

514

System.out.println("Hyperlink found: " + extra.getText());

515

break;

516

case MERGE:

517

System.out.println("Merged region: " +

518

extra.getFirstRowIndex() + "-" + extra.getLastRowIndex() +

519

", " + extra.getFirstColumnIndex() + "-" + extra.getLastColumnIndex());

520

break;

521

}

522

}

523

524

@Override

525

public void doAfterAllAnalysed(AnalysisContext context) {

526

System.out.println("Headers processed: " + headers.size());

527

System.out.println("Comments found: " + comments.size());

528

comments.forEach(System.out::println);

529

}

530

}

531

532

// Usage with extra information reading

533

EasyExcel.read("data-with-extras.xlsx", UserData.class, new DetailedDataListener())

534

.extraRead(CellExtraTypeEnum.COMMENT)

535

.extraRead(CellExtraTypeEnum.HYPERLINK)

536

.extraRead(CellExtraTypeEnum.MERGE)

537

.sheet()

538

.doRead();

539

```

540

541

### Error-Tolerant Processing

542

```java

543

import com.alibaba.excel.context.AnalysisContext;

544

import com.alibaba.excel.event.AnalysisEventListener;

545

546

public class RobustDataListener extends AnalysisEventListener<UserData> {

547

private int successCount = 0;

548

private int errorCount = 0;

549

private List<String> errors = new ArrayList<>();

550

551

@Override

552

public void invoke(UserData data, AnalysisContext context) {

553

try {

554

// Process data with potential validation

555

validateAndProcess(data);

556

successCount++;

557

} catch (Exception e) {

558

errorCount++;

559

String error = "Row " + context.getCurrentRowNum() + ": " + e.getMessage();

560

errors.add(error);

561

System.err.println(error);

562

}

563

}

564

565

@Override

566

public void onException(Exception exception, AnalysisContext context) {

567

errorCount++;

568

String error = "Parse error at row " + context.getCurrentRowNum() +

569

": " + exception.getMessage();

570

errors.add(error);

571

System.err.println(error);

572

573

// Continue processing instead of stopping

574

}

575

576

@Override

577

public void doAfterAllAnalysed(AnalysisContext context) {

578

System.out.println("Processing completed:");

579

System.out.println("- Successful rows: " + successCount);

580

System.out.println("- Error rows: " + errorCount);

581

582

if (!errors.isEmpty()) {

583

System.out.println("Errors encountered:");

584

errors.forEach(System.out::println);

585

}

586

}

587

588

private void validateAndProcess(UserData data) {

589

if (data.getName() == null || data.getName().trim().isEmpty()) {

590

throw new IllegalArgumentException("Name is required");

591

}

592

if (data.getEmail() == null || !data.getEmail().contains("@")) {

593

throw new IllegalArgumentException("Valid email is required");

594

}

595

596

// Process valid data

597

saveUser(data);

598

}

599

600

private void saveUser(UserData user) {

601

// Implementation for saving user

602

}

603

}

604

```

605

606

### Conditional Reading Control

607

```java

608

import com.alibaba.excel.context.AnalysisContext;

609

import com.alibaba.excel.event.AnalysisEventListener;

610

611

public class ConditionalReadListener extends AnalysisEventListener<UserData> {

612

private int maxRows = 1000;

613

private int processedRows = 0;

614

private boolean stopReading = false;

615

616

@Override

617

public void invoke(UserData data, AnalysisContext context) {

618

processedRows++;

619

620

// Process data

621

System.out.println("Processing row " + processedRows + ": " + data.getName());

622

623

// Check stopping condition

624

if (processedRows >= maxRows) {

625

System.out.println("Reached maximum rows limit: " + maxRows);

626

stopReading = true;

627

}

628

629

// Could also stop based on data content

630

if ("STOP".equals(data.getName())) {

631

System.out.println("Found stop marker, ending processing");

632

stopReading = true;

633

}

634

}

635

636

@Override

637

public boolean hasNext(AnalysisContext context) {

638

return !stopReading;

639

}

640

641

@Override

642

public void doAfterAllAnalysed(AnalysisContext context) {

643

System.out.println("Processing ended. Total rows processed: " + processedRows);

644

}

645

}

646

```