or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-node-types.mdcopy-paste-detection.mdindex.mdjsp-parser-ast.mdlanguage-module.mdrule-development.mdvisitor-pattern.md

visitor-pattern.mddocs/

0

# Visitor Pattern

1

2

The JSP AST uses the visitor pattern to enable traversal and analysis of JSP documents. The visitor pattern provides a clean way to implement operations on AST nodes without modifying the node classes themselves.

3

4

## Visitor Interface

5

6

### JspVisitor

7

8

Core visitor interface for JSP AST traversal. This interface is generated by JavaCC during the build process and provides type-safe visit methods for all AST node types.

9

10

```java { .api }

11

public interface JspVisitor<P, R> {

12

// Generated interface with visit methods for all AST node types

13

// This interface is generated by JavaCC during the build process

14

// and contains visit methods for each concrete AST node class

15

16

R visit(ASTCompilationUnit node, P data);

17

R visit(ASTElement node, P data);

18

R visit(ASTAttribute node, P data);

19

R visit(ASTAttributeValue node, P data);

20

R visit(ASTCData node, P data);

21

R visit(ASTCommentTag node, P data);

22

R visit(ASTContent node, P data);

23

R visit(ASTDeclaration node, P data);

24

R visit(ASTDoctypeDeclaration node, P data);

25

R visit(ASTDoctypeExternalId node, P data);

26

R visit(ASTElExpression node, P data);

27

R visit(ASTHtmlScript node, P data);

28

R visit(ASTJspComment node, P data);

29

R visit(ASTJspDeclaration node, P data);

30

R visit(ASTJspDirective node, P data);

31

R visit(ASTJspDirectiveAttribute node, P data);

32

R visit(ASTJspExpression node, P data);

33

R visit(ASTJspExpressionInAttribute node, P data);

34

R visit(ASTJspScriptlet node, P data);

35

R visit(ASTText node, P data);

36

R visit(ASTUnparsedText node, P data);

37

R visit(ASTValueBinding node, P data);

38

}

39

```

40

41

**Type Parameters:**

42

- `P`: Parameter type passed to visit methods (often RuleContext for rules)

43

- `R`: Return type from visit methods (often the same as P, or void)

44

45

## Base Visitor Implementation

46

47

### JspVisitorBase

48

49

Base implementation providing default visitor behavior.

50

51

```java { .api }

52

public class JspVisitorBase<P, R> extends AstVisitorBase<P, R> implements JspVisitor<P, R> {

53

}

54

```

55

56

**Usage:**

57

58

```java

59

import net.sourceforge.pmd.lang.jsp.ast.JspVisitorBase;

60

import net.sourceforge.pmd.lang.jsp.ast.*;

61

62

public class MyJspVisitor extends JspVisitorBase<Void, Void> {

63

64

@Override

65

public Void visit(ASTElement node, Void data) {

66

System.out.println("Found element: " + node.getName());

67

return super.visit(node, data); // Continue traversal

68

}

69

70

@Override

71

public Void visit(ASTJspExpression node, Void data) {

72

System.out.println("Found JSP expression: " + node.getContent());

73

return super.visit(node, data);

74

}

75

}

76

```

77

78

## Visitor Dispatch

79

80

### AbstractJspNode Visitor Support

81

82

All JSP AST nodes support visitor dispatch through the visitor pattern.

83

84

```java { .api }

85

public abstract class AbstractJspNode extends AbstractJjtreeNode<AbstractJspNode, JspNode> implements JspNode {

86

public final <P, R> R acceptVisitor(AstVisitor<? super P, ? extends R> visitor, P data);

87

protected abstract <P, R> R acceptVisitor(JspVisitor<? super P, ? extends R> visitor, P data);

88

}

89

```

90

91

**Usage:**

92

93

```java

94

import net.sourceforge.pmd.lang.jsp.ast.ASTCompilationUnit;

95

96

// Apply visitor to AST

97

MyJspVisitor visitor = new MyJspVisitor();

98

compilationUnit.acceptVisitor(visitor, null);

99

```

100

101

**Methods:**

102

103

- `acceptVisitor(AstVisitor, P)`: Generic visitor dispatch that checks for JSP visitor capability

104

- `acceptVisitor(JspVisitor, P)`: Direct JSP visitor dispatch implemented by each node type

105

106

## Visitor Implementation Patterns

107

108

### Simple Analysis Visitor

109

110

```java

111

import net.sourceforge.pmd.lang.jsp.ast.*;

112

113

public class JspAnalysisVisitor extends JspVisitorBase<Void, Void> {

114

115

private int elementCount = 0;

116

private int expressionCount = 0;

117

118

@Override

119

public Void visit(ASTElement node, Void data) {

120

elementCount++;

121

122

// Analyze element properties

123

if (node.isUnclosed()) {

124

System.out.println("Warning: Unclosed element " + node.getName());

125

}

126

127

if ("script".equals(node.getName())) {

128

System.out.println("Found script element");

129

}

130

131

return super.visit(node, data);

132

}

133

134

@Override

135

public Void visit(ASTJspExpression node, Void data) {

136

expressionCount++;

137

138

String content = node.getContent();

139

if (content.contains("request.")) {

140

System.out.println("Found request access: " + content);

141

}

142

143

return super.visit(node, data);

144

}

145

146

@Override

147

public Void visit(ASTElExpression node, Void data) {

148

expressionCount++;

149

150

String content = node.getContent();

151

System.out.println("EL Expression: " + content);

152

153

return super.visit(node, data);

154

}

155

156

public void printStats() {

157

System.out.println("Elements found: " + elementCount);

158

System.out.println("Expressions found: " + expressionCount);

159

}

160

}

161

```

162

163

### Data Collection Visitor

164

165

```java

166

import java.util.*;

167

168

public class JspDataCollector extends JspVisitorBase<Void, Void> {

169

170

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

171

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

172

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

173

private Map<String, List<String>> elementAttributes = new HashMap<>();

174

175

@Override

176

public Void visit(ASTElement node, Void data) {

177

String elementName = node.getName();

178

elementNames.add(elementName);

179

180

// Collect attributes for this element

181

List<String> attrs = node.children(ASTAttribute.class)

182

.map(ASTAttribute::getName)

183

.collect(Collectors.toList());

184

185

if (!attrs.isEmpty()) {

186

elementAttributes.put(elementName, attrs);

187

}

188

189

return super.visit(node, data);

190

}

191

192

@Override

193

public Void visit(ASTJspExpression node, Void data) {

194

jspExpressions.add(node.getContent());

195

return super.visit(node, data);

196

}

197

198

@Override

199

public Void visit(ASTElExpression node, Void data) {

200

elExpressions.add(node.getContent());

201

return super.visit(node, data);

202

}

203

204

// Getters for collected data

205

public List<String> getElementNames() { return elementNames; }

206

public List<String> getJspExpressions() { return jspExpressions; }

207

public List<String> getElExpressions() { return elExpressions; }

208

public Map<String, List<String>> getElementAttributes() { return elementAttributes; }

209

}

210

```

211

212

### Conditional Visitor

213

214

```java

215

public class SecurityAnalysisVisitor extends JspVisitorBase<List<String>, List<String>> {

216

217

@Override

218

public List<String> visit(ASTElExpression node, List<String> issues) {

219

String content = node.getContent();

220

221

// Check for potentially unsafe EL expressions

222

if (!isInTaglib(node) && !isSanitized(content)) {

223

issues.add("Potentially unsafe EL expression: " + content +

224

" at line " + node.getBeginLine());

225

}

226

227

return super.visit(node, issues);

228

}

229

230

@Override

231

public List<String> visit(ASTElement node, List<String> issues) {

232

// Check for inline event handlers

233

if (hasInlineEventHandler(node)) {

234

issues.add("Inline event handler found in " + node.getName() +

235

" at line " + node.getBeginLine());

236

}

237

238

return super.visit(node, issues);

239

}

240

241

private boolean isInTaglib(ASTElExpression node) {

242

// Check if EL expression is within a taglib element

243

return node.ancestors(ASTElement.class)

244

.anyMatch(elem -> elem.getNamespacePrefix() != null);

245

}

246

247

private boolean isSanitized(String content) {

248

return content.matches("^fn:escapeXml\\(.+\\)$");

249

}

250

251

private boolean hasInlineEventHandler(ASTElement element) {

252

return element.children(ASTAttribute.class)

253

.anyMatch(attr -> attr.getName().startsWith("on")); // onclick, onload, etc.

254

}

255

}

256

```

257

258

## Advanced Visitor Techniques

259

260

### Visitor with Context

261

262

```java

263

public class ContextAwareVisitor extends JspVisitorBase<VisitorContext, Void> {

264

265

public static class VisitorContext {

266

private Stack<String> elementStack = new Stack<>();

267

private Set<String> seenIds = new HashSet<>();

268

269

public void pushElement(String name) { elementStack.push(name); }

270

public String popElement() { return elementStack.pop(); }

271

public String getCurrentElement() {

272

return elementStack.isEmpty() ? null : elementStack.peek();

273

}

274

public boolean addId(String id) { return seenIds.add(id); }

275

}

276

277

@Override

278

public Void visit(ASTElement node, VisitorContext context) {

279

String elementName = node.getName();

280

context.pushElement(elementName);

281

282

// Check for duplicate IDs

283

node.children(ASTAttribute.class)

284

.filter(attr -> "id".equals(attr.getName()))

285

.forEach(attr -> {

286

String id = getAttributeValue(attr);

287

if (!context.addId(id)) {

288

System.out.println("Duplicate ID found: " + id);

289

}

290

});

291

292

// Continue traversal

293

super.visit(node, context);

294

295

context.popElement();

296

return null;

297

}

298

299

private String getAttributeValue(ASTAttribute attr) {

300

return attr.children(ASTAttributeValue.class)

301

.findFirst()

302

.map(ASTAttributeValue::getValue)

303

.orElse("");

304

}

305

}

306

```

307

308

### Visitor Composition

309

310

```java

311

public class CompositeVisitor extends JspVisitorBase<Void, Void> {

312

313

private List<JspVisitor<Void, Void>> visitors = new ArrayList<>();

314

315

public void addVisitor(JspVisitor<Void, Void> visitor) {

316

visitors.add(visitor);

317

}

318

319

@Override

320

public Void visit(ASTElement node, Void data) {

321

// Apply all visitors to this node

322

for (JspVisitor<Void, Void> visitor : visitors) {

323

node.acceptVisitor(visitor, data);

324

}

325

return super.visit(node, data);

326

}

327

328

// Override other visit methods similarly...

329

}

330

```

331

332

## Integration with PMD Rules

333

334

The visitor pattern integrates seamlessly with PMD's rule framework:

335

336

```java

337

import net.sourceforge.pmd.lang.jsp.rule.AbstractJspRule;

338

339

public class CustomJspRule extends AbstractJspRule {

340

341

@Override

342

public Object visit(ASTElExpression node, Object data) {

343

// Rule-specific logic

344

if (violatesRule(node)) {

345

asCtx(data).addViolation(node, "Rule violation message");

346

}

347

348

return super.visit(node, data);

349

}

350

351

private boolean violatesRule(ASTElExpression node) {

352

// Custom rule logic

353

return node.getContent().contains("unsafeMethod");

354

}

355

}

356

```

357

358

## Best Practices

359

360

### Performance Considerations

361

362

```java

363

// Avoid creating objects in visit methods for frequently called nodes

364

@Override

365

public Void visit(ASTText node, Void data) {

366

// Good: Direct operations

367

if (node.getContent().trim().isEmpty()) {

368

// Handle empty text

369

}

370

371

// Avoid: Creating unnecessary objects

372

// String trimmed = node.getContent().trim(); // Creates new string

373

374

return super.visit(node, data);

375

}

376

```

377

378

### Null Safety

379

380

```java

381

@Override

382

public Void visit(ASTElement node, Void data) {

383

String name = node.getName();

384

if (name != null && name.equals("script")) {

385

// Safe null check

386

}

387

388

return super.visit(node, data);

389

}

390

```

391

392

### Selective Traversal

393

394

```java

395

@Override

396

public Void visit(ASTElement node, Void data) {

397

if ("script".equals(node.getName())) {

398

// Don't traverse into script elements

399

return null;

400

}

401

402

return super.visit(node, data); // Continue normal traversal

403

}

404

```

405

406

The visitor pattern provides a powerful and flexible way to analyze JSP AST structures while keeping analysis logic separate from the AST node implementations.