Passed
Push — master ( e0b928...937cde )
by Julian
03:04 queued 12s
created

de.tudresden.inf.lat.jcel.core.algorithm.rulebased.ClassifierStatusImpl   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 505
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 269
c 1
b 0
f 0
dl 0
loc 505
rs 4.08
wmc 59

42 Methods

Rating   Name   Duplication   Size   Complexity  
A getInverseObjectPropertyOf(int) 0 3 1
A addToS(int,int) 0 6 1
A deleteObjectPropertyGraph() 0 2 1
A createObjectPropertyGraph() 0 17 1
A contains(VNode) 0 8 2
A getExtendedOntology() 0 3 1
A getRelationSetMonitor() 0 3 1
A createClassGraph() 0 10 1
A createRelationSet() 0 5 1
A getDeepSizeOfS() 0 6 1
A getObjectPropertiesWithFunctionalAncestor(int) 0 9 2
A outputSetS(Writer) 0 13 3
A getDeepSizeOfR() 0 6 1
A getDeepSizeOfV() 0 3 1
A getObjectPropertyGraph() 0 2 1
A deleteClassGraph() 0 3 1
A getSecondByFirst(int,int) 0 7 1
A getObjectPropertiesByFirst(int) 0 7 1
A getSizeOfV() 0 2 1
A getSubsumers(int) 0 7 1
A getNode(int) 0 8 2
A ClassifierStatusImpl(IntegerEntityManager,ExtendedOntology) 0 11 1
A createSetOfNodes() 0 2 1
A getNumberOfSEntries() 0 6 1
A getClassGraph() 0 2 1
A createOrGetNodeId(VNode) 0 18 4
A addNewSEntry(int,int) 0 7 1
A getIdGenerator() 0 2 1
A createMapOfObjectPropertiesWithFunctionalAncestor() 0 10 2
A getNumberOfREntries() 0 6 1
A getObjectPropertiesBySecond(int) 0 7 1
A removeNextSEntry() 0 11 2
A addToR(int,int,int) 0 6 1
A removeNextREntry() 0 11 2
A getSubObjectProperties(int) 0 3 1
A getClassGraphMonitor() 0 3 1
A addNewREntry(int,int,int) 0 7 1
A getFirstBySecond(int,int) 0 7 1
A outputSetR(Writer) 0 18 4
A getRelationSet() 0 2 1
A makeTransitiveClosure(IntegerSubsumerBidirectionalGraphImpl) 0 16 4
A getSuperObjectProperties(int) 0 3 1

How to fix   Complexity   

Complexity

Complex classes like de.tudresden.inf.lat.jcel.core.algorithm.rulebased.ClassifierStatusImpl often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*
2
 *
3
 * Copyright (C) 2009-2017 Julian Mendez
4
 *
5
 *
6
 * This file is part of jcel.
7
 *
8
 *
9
 * The contents of this file are subject to the GNU Lesser General Public License
10
 * version 3
11
 *
12
 *
13
 * This program is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Lesser General Public License as published by
15
 * the Free Software Foundation, either version 3 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU Lesser General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Lesser General Public License
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25
 *
26
 *
27
 * Alternatively, the contents of this file may be used under the terms
28
 * of the Apache License, Version 2.0, in which case the
29
 * provisions of the Apache License, Version 2.0 are applicable instead of those
30
 * above.
31
 *
32
 *
33
 * Licensed under the Apache License, Version 2.0 (the "License");
34
 * you may not use this file except in compliance with the License.
35
 * You may obtain a copy of the License at
36
 *
37
 *     http://www.apache.org/licenses/LICENSE-2.0
38
 *
39
 * Unless required by applicable law or agreed to in writing, software
40
 * distributed under the License is distributed on an "AS IS" BASIS,
41
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42
 * See the License for the specific language governing permissions and
43
 * limitations under the License.
44
 *
45
 */
46
47
package de.tudresden.inf.lat.jcel.core.algorithm.rulebased;
48
49
import java.io.BufferedWriter;
50
import java.io.IOException;
51
import java.io.Writer;
52
import java.util.Collection;
53
import java.util.Collections;
54
import java.util.HashMap;
55
import java.util.HashSet;
56
import java.util.NoSuchElementException;
57
import java.util.Objects;
58
import java.util.Optional;
59
import java.util.Set;
60
import java.util.TreeSet;
61
62
import de.tudresden.inf.lat.jcel.core.completion.common.ClassifierStatus;
63
import de.tudresden.inf.lat.jcel.core.completion.common.REntry;
64
import de.tudresden.inf.lat.jcel.core.completion.common.SEntry;
65
import de.tudresden.inf.lat.jcel.core.graph.IntegerRelationMapImpl;
66
import de.tudresden.inf.lat.jcel.core.graph.IntegerSubsumerBidirectionalGraphImpl;
67
import de.tudresden.inf.lat.jcel.core.graph.IntegerSubsumerGraphImpl;
68
import de.tudresden.inf.lat.jcel.core.graph.VNode;
69
import de.tudresden.inf.lat.jcel.core.graph.VNodeImpl;
70
import de.tudresden.inf.lat.jcel.coreontology.axiom.ExtendedOntology;
71
import de.tudresden.inf.lat.jcel.coreontology.axiom.RI2Axiom;
72
import de.tudresden.inf.lat.jcel.coreontology.datatype.IntegerEntityManager;
73
import de.tudresden.inf.lat.jcel.coreontology.datatype.IntegerEntityType;
74
import de.tudresden.inf.lat.util.map.OptMap;
75
import de.tudresden.inf.lat.util.map.OptMapImpl;
76
77
/**
78
 * An object of this class keeps the status of the classifier.
79
 * 
80
 * @author Julian Mendez
81
 */
82
public class ClassifierStatusImpl implements ClassifierStatus {
83
84
	private static final String COMMA_SEPARATOR = ",";
85
86
	private static final int bottomClassId = IntegerEntityManager.bottomClassId;
87
	private static final int bottomObjectPropertyId = IntegerEntityManager.bottomObjectPropertyId;
88
	private static final int topClassId = IntegerEntityManager.topClassId;
89
	private static final int topObjectPropertyId = IntegerEntityManager.topObjectPropertyId;
90
91
	private IntegerSubsumerGraphImpl classGraph = null;
92
	private final OptMap<Integer, Set<Integer>> cognateFunctPropMap = new OptMapImpl<>(new HashMap<>());
93
	private final ExtendedOntology extendedOntology;
94
	private IntegerEntityManager entityManager = null;
95
	private final OptMap<VNodeImpl, Integer> invNodeSet = new OptMapImpl<>(new HashMap<>());
96
	private final Object monitorClassGraph = new Object();
97
	private final Object monitorRelationSet = new Object();
98
	private final Object monitorSetQsubR = new Object();
99
	private final Object monitorSetQsubS = new Object();
100
	private final OptMap<Integer, VNodeImpl> nodeSet = new OptMapImpl<>(new HashMap<>());
101
	private IntegerSubsumerBidirectionalGraphImpl objectPropertyGraph = null;
102
	private IntegerRelationMapImpl relationSet = null;
103
	private final Set<REntry> setQsubR = new TreeSet<>();
104
	private final Set<SEntry> setQsubS = new TreeSet<>();
105
106
	/**
107
	 * Constructs a new classifier status.
108
	 * 
109
	 * @param generator
110
	 *            identifier generator
111
	 * @param ontology
112
	 *            extended ontology
113
	 */
114
	public ClassifierStatusImpl(IntegerEntityManager generator, ExtendedOntology ontology) {
115
		Objects.requireNonNull(generator);
116
		Objects.requireNonNull(ontology);
117
		this.entityManager = generator;
118
		this.extendedOntology = ontology;
119
120
		createClassGraph();
121
		createObjectPropertyGraph();
122
		createRelationSet();
123
		createSetOfNodes();
124
		createMapOfObjectPropertiesWithFunctionalAncestor();
125
	}
126
127
	@Override
128
	public boolean addNewREntry(int propertyId, int leftClassId, int rightClassId) {
129
		boolean ret = false;
130
		synchronized (this.monitorSetQsubR) {
131
			ret = this.setQsubR.add(new REntryImpl(propertyId, leftClassId, rightClassId));
132
		}
133
		return ret;
134
	}
135
136
	@Override
137
	public boolean addNewSEntry(int subClassId, int superClassId) {
138
		boolean ret = false;
139
		synchronized (this.monitorSetQsubS) {
140
			ret = this.setQsubS.add(new SEntryImpl(subClassId, superClassId));
141
		}
142
		return ret;
143
	}
144
145
	/**
146
	 * Adds a new triplet to the set R.
147
	 * 
148
	 * @param property
149
	 *            property
150
	 * @param leftClass
151
	 *            left class
152
	 * @param rightClass
153
	 *            right class
154
	 * @return <code>true</code> if the triplet was effectively added,
155
	 *         <code>false</code> otherwise
156
	 */
157
	public boolean addToR(int property, int leftClass, int rightClass) {
158
		boolean ret = false;
159
		synchronized (this.monitorRelationSet) {
160
			ret = this.relationSet.add(property, leftClass, rightClass);
161
		}
162
		return ret;
163
	}
164
165
	/**
166
	 * Adds a new pair to the set S.
167
	 * 
168
	 * @param subClass
169
	 *            sub class
170
	 * @param superClass
171
	 *            super class
172
	 * @return <code>true</code> if the pair was effectively added,
173
	 *         <code>false</code> otherwise
174
	 */
175
	public boolean addToS(int subClass, int superClass) {
176
		boolean ret = false;
177
		synchronized (this.monitorClassGraph) {
178
			ret = this.classGraph.addAncestor(subClass, superClass);
179
		}
180
		return ret;
181
	}
182
183
	@Override
184
	public boolean contains(VNode node) {
185
		Objects.requireNonNull(node);
186
		boolean ret = false;
187
		if (node instanceof VNodeImpl) {
188
			ret = this.invNodeSet.get((VNodeImpl) node).isPresent();
189
		}
190
		return ret;
191
	}
192
193
	private void createClassGraph() {
194
		synchronized (this.monitorClassGraph) {
195
			this.classGraph = new IntegerSubsumerGraphImpl(bottomClassId, topClassId);
196
		}
197
		this.nodeSet.clear();
198
		this.invNodeSet.clear();
199
		getExtendedOntology().getClassSet().forEach(elem -> {
200
			VNodeImpl node = new VNodeImpl(elem);
201
			this.nodeSet.put(elem, node);
202
			this.invNodeSet.put(node, elem);
203
		});
204
	}
205
206
	private void createMapOfObjectPropertiesWithFunctionalAncestor() {
207
		this.extendedOntology.getFunctionalObjectProperties().forEach(s -> {
208
			Collection<Integer> cognates = getSubObjectProperties(s);
209
			cognates.forEach(r -> {
210
				Optional<Set<Integer>> optCurrentSet = this.cognateFunctPropMap.get(r);
211
				if (!optCurrentSet.isPresent()) {
212
					optCurrentSet = Optional.of(new HashSet<>());
213
					this.cognateFunctPropMap.put(r, optCurrentSet.get());
214
				}
215
				optCurrentSet.get().addAll(cognates);
216
			});
217
		});
218
	}
219
220
	private void createObjectPropertyGraph() {
221
		this.objectPropertyGraph = new IntegerSubsumerBidirectionalGraphImpl(bottomObjectPropertyId,
222
				topObjectPropertyId);
223
224
		this.extendedOntology.getObjectPropertySet().forEach(index -> {
225
			this.objectPropertyGraph.addAncestor(index, topObjectPropertyId);
226
			int inverseProp = this.entityManager.createOrGetInverseObjectPropertyOf(index);
227
			this.objectPropertyGraph.addAncestor(inverseProp, topObjectPropertyId);
228
		});
229
230
		this.extendedOntology.getObjectPropertySet().forEach(property -> {
231
			Set<RI2Axiom> axiomSet = this.extendedOntology.getRI2rAxioms(property);
232
			axiomSet.forEach(
233
					axiom -> this.objectPropertyGraph.addAncestor(axiom.getSubProperty(), axiom.getSuperProperty()));
234
		});
235
236
		makeTransitiveClosure(this.objectPropertyGraph);
237
	}
238
239
	@Override
240
	public int createOrGetNodeId(VNode node) {
241
		Objects.requireNonNull(node);
242
		Optional<Integer> optNodeId = Optional.empty();
243
		if (node instanceof VNodeImpl) {
244
			optNodeId = this.invNodeSet.get((VNodeImpl) node);
245
		}
246
		if (!optNodeId.isPresent()) {
247
			optNodeId = Optional.of(node.getClassId());
248
			if (!node.isEmpty()) {
249
				optNodeId = Optional.of(getIdGenerator().createAnonymousEntity(IntegerEntityType.CLASS, true));
250
				VNodeImpl newNode = new VNodeImpl(node.getClassId());
251
				newNode.addExistentialsOf(node);
252
				this.nodeSet.put(optNodeId.get(), newNode);
253
				this.invNodeSet.put(newNode, optNodeId.get());
254
			}
255
		}
256
		return optNodeId.get();
257
	}
258
259
	private void createRelationSet() {
260
		Collection<Integer> collection = getObjectPropertyGraph().getElements();
261
		synchronized (this.monitorRelationSet) {
262
			this.relationSet = new IntegerRelationMapImpl();
263
			collection.forEach(index -> this.relationSet.add(index));
264
		}
265
	}
266
267
	private void createSetOfNodes() {
268
		getExtendedOntology().getClassSet().forEach(classId -> createOrGetNodeId(new VNodeImpl(classId)));
269
	}
270
271
	/**
272
	 * Deletes the class graph.
273
	 */
274
	protected void deleteClassGraph() {
275
		synchronized (this.monitorClassGraph) {
276
			this.classGraph = null;
277
		}
278
	}
279
280
	/**
281
	 * Deletes the object property graph.
282
	 */
283
	protected void deleteObjectPropertyGraph() {
284
		this.objectPropertyGraph = null;
285
	}
286
287
	/**
288
	 * Returns the class graph.
289
	 * 
290
	 * @return the class graph
291
	 */
292
	protected IntegerSubsumerGraphImpl getClassGraph() {
293
		return this.classGraph;
294
	}
295
296
	@Override
297
	public Object getClassGraphMonitor() {
298
		return this.monitorClassGraph;
299
	}
300
301
	/**
302
	 * Returns the number of nodes in the relation set.
303
	 * 
304
	 * @return the number of nodes in the relation set
305
	 */
306
	public long getDeepSizeOfR() {
307
		long ret;
308
		synchronized (this.monitorRelationSet) {
309
			ret = this.relationSet.getDeepSize();
310
		}
311
		return ret;
312
	}
313
314
	/**
315
	 * Returns the number of nodes in the subsumer set.
316
	 * 
317
	 * @return the number of nodes in the subsumer set
318
	 */
319
	public long getDeepSizeOfS() {
320
		long ret;
321
		synchronized (this.monitorClassGraph) {
322
			ret = this.classGraph.getDeepSize();
323
		}
324
		return ret;
325
	}
326
327
	/**
328
	 * Returns the number of elements in the node set.
329
	 * 
330
	 * @return the number of elements in the node set
331
	 */
332
	public long getDeepSizeOfV() {
333
		return this.nodeSet.keySet().stream().map(nodeId -> this.nodeSet.get(nodeId).get().getDeepSize()).reduce(0L,
334
				(accum, elem) -> (accum + elem));
335
	}
336
337
	@Override
338
	public ExtendedOntology getExtendedOntology() {
339
		return this.extendedOntology;
340
	}
341
342
	@Override
343
	public Collection<Integer> getFirstBySecond(int propertyId, int classId) {
344
		Collection<Integer> ret = new TreeSet<>();
345
		synchronized (this.monitorRelationSet) {
346
			ret.addAll(this.relationSet.getBySecond(propertyId, classId));
347
		}
348
		return ret;
349
	}
350
351
	/**
352
	 * Returns the identifier generator.
353
	 * 
354
	 * @return the identifier generator
355
	 */
356
	protected IntegerEntityManager getIdGenerator() {
357
		return this.entityManager;
358
	}
359
360
	@Override
361
	public int getInverseObjectPropertyOf(int propertyId) {
362
		return this.entityManager.createOrGetInverseObjectPropertyOf(propertyId);
363
	}
364
365
	@Override
366
	public Optional<VNode> getNode(int nodeId) {
367
		Optional<VNodeImpl> node = this.nodeSet.get(nodeId);
368
		Optional<VNode> ret = Optional.empty();
369
		if (node.isPresent()) {
370
			ret = Optional.of(node.get());
371
		}
372
		return ret;
373
	}
374
375
	/**
376
	 * Returns the number of R-entries to be processed.
377
	 * 
378
	 * @return the number of R-entries to be processed
379
	 */
380
	public int getNumberOfREntries() {
381
		int ret = 0;
382
		synchronized (this.monitorSetQsubR) {
383
			ret = this.setQsubR.size();
384
		}
385
		return ret;
386
	}
387
388
	/**
389
	 * Returns the number of S-entries to be processed.
390
	 * 
391
	 * @return the number of S-entries to be processed
392
	 */
393
394
	public int getNumberOfSEntries() {
395
		int ret = 0;
396
		synchronized (this.monitorSetQsubS) {
397
			ret = this.setQsubS.size();
398
		}
399
		return ret;
400
	}
401
402
	@Override
403
	public Collection<Integer> getObjectPropertiesByFirst(int cA) {
404
		Collection<Integer> ret = new TreeSet<>();
405
		synchronized (this.monitorRelationSet) {
406
			ret.addAll(this.relationSet.getRelationsByFirst(cA));
407
		}
408
		return ret;
409
	}
410
411
	@Override
412
	public Collection<Integer> getObjectPropertiesBySecond(int cA) {
413
		Collection<Integer> ret = new TreeSet<>();
414
		synchronized (this.monitorRelationSet) {
415
			ret.addAll(this.relationSet.getRelationsBySecond(cA));
416
		}
417
		return ret;
418
	}
419
420
	@Override
421
	public Set<Integer> getObjectPropertiesWithFunctionalAncestor(int objectProperty) {
422
		Optional<Set<Integer>> optSet = this.cognateFunctPropMap.get(objectProperty);
423
		if (!optSet.isPresent()) {
424
			optSet = Optional.of(Collections.emptySet());
425
		} else {
426
			optSet = Optional.of(Collections.unmodifiableSet(optSet.get()));
427
		}
428
		return optSet.get();
429
	}
430
431
	/**
432
	 * Returns the object property graph.
433
	 * 
434
	 * @return the object property graph
435
	 */
436
	protected IntegerSubsumerBidirectionalGraphImpl getObjectPropertyGraph() {
437
		return this.objectPropertyGraph;
438
	}
439
440
	/**
441
	 * Returns the set of relations.
442
	 * 
443
	 * @return the set of relations
444
	 */
445
	protected IntegerRelationMapImpl getRelationSet() {
446
		return this.relationSet;
447
	}
448
449
	@Override
450
	public Object getRelationSetMonitor() {
451
		return this.monitorRelationSet;
452
	}
453
454
	@Override
455
	public Collection<Integer> getSecondByFirst(int propertyId, int classId) {
456
		Collection<Integer> ret = new TreeSet<>();
457
		synchronized (this.monitorRelationSet) {
458
			ret.addAll(this.relationSet.getByFirst(propertyId, classId));
459
		}
460
		return ret;
461
	}
462
463
	/**
464
	 * Returns the number of nodes.
465
	 * 
466
	 * @return the number of nodes
467
	 */
468
	public long getSizeOfV() {
469
		return this.nodeSet.size();
470
	}
471
472
	@Override
473
	public Collection<Integer> getSubObjectProperties(int objectProperty) {
474
		return Collections.unmodifiableCollection(this.objectPropertyGraph.getSubsumees(objectProperty));
475
	}
476
477
	@Override
478
	public Collection<Integer> getSubsumers(int classId) {
479
		Collection<Integer> ret = null;
480
		synchronized (this.monitorClassGraph) {
481
			ret = this.classGraph.getSubsumers(classId);
482
		}
483
		return ret;
484
	}
485
486
	@Override
487
	public Collection<Integer> getSuperObjectProperties(int objectProperty) {
488
		return Collections.unmodifiableCollection(this.objectPropertyGraph.getSubsumers(objectProperty));
489
	}
490
491
	private void makeTransitiveClosure(IntegerSubsumerBidirectionalGraphImpl graph) {
492
		boolean hasChanged = true;
493
		while (hasChanged) {
494
			hasChanged = false;
495
			for (int elem : graph.getElements()) {
496
				Collection<Integer> subsumerSet = graph.getSubsumers(elem);
497
				Set<Integer> allSubsumers = new HashSet<>();
498
				allSubsumers.add(elem);
499
				subsumerSet.forEach(otherElem -> {
500
					allSubsumers.addAll(graph.getSubsumers(otherElem));
501
				});
502
				allSubsumers.removeAll(subsumerSet);
503
				if (!allSubsumers.isEmpty()) {
504
					hasChanged = true;
505
					allSubsumers.forEach(subsumer -> {
506
						graph.addAncestor(elem, subsumer);
507
					});
508
				}
509
			}
510
		}
511
	}
512
513
	/**
514
	 * Returns the next R-entry and removes it from the set to be processed.
515
	 * 
516
	 * @return the next R-entry and removes it from the set to be processed
517
	 * 
518
	 * @throws NoSuchElementException
519
	 *             if the set of R-entries is empty
520
	 */
521
	public REntry removeNextREntry() {
522
		REntry ret = null;
523
		synchronized (this.monitorSetQsubR) {
524
			if (this.setQsubR.isEmpty()) {
525
				throw new NoSuchElementException();
526
			}
527
528
			ret = this.setQsubR.iterator().next();
529
			this.setQsubR.remove(ret);
530
		}
531
		return ret;
532
	}
533
534
	/**
535
	 * Returns the next S-entry and removes it from the set to be processed.
536
	 * 
537
	 * @return the next S-entry and removes it from the set to be processed
538
	 * @throws NoSuchElementException
539
	 *             if the set of S-entries is empty
540
	 */
541
	public SEntry removeNextSEntry() {
542
		SEntry ret = null;
543
		synchronized (this.monitorSetQsubS) {
544
			if (this.setQsubS.isEmpty()) {
545
				throw new NoSuchElementException();
546
			}
547
548
			ret = this.setQsubS.iterator().next();
549
			this.setQsubS.remove(ret);
550
		}
551
		return ret;
552
	}
553
554
	public void outputSetS(Writer output) throws IOException {
555
		BufferedWriter writer = new BufferedWriter(output);
556
		Collection<Integer> concepts = this.classGraph.getElements();
557
		for (Integer concept : concepts) {
558
			Collection<Integer> subsumers = this.classGraph.getSubsumers(concept);
559
			for (Integer subsumer : subsumers) {
560
				writer.write(this.entityManager.getName(concept));
561
				writer.write(COMMA_SEPARATOR);
562
				writer.write(this.entityManager.getName(subsumer));
563
				writer.newLine();
564
			}
565
		}
566
		writer.flush();
567
	}
568
569
	public void outputSetR(Writer output) throws IOException {
570
		BufferedWriter writer = new BufferedWriter(output);
571
		Collection<Integer> concepts = this.classGraph.getElements();
572
		for (Integer concept : concepts) {
573
			Collection<Integer> relations = this.relationSet.getRelationsByFirst(concept);
574
			for (Integer relation : relations) {
575
				Collection<Integer> otherConcepts = this.relationSet.getByFirst(relation, concept);
576
				for (Integer otherConcept : otherConcepts) {
577
					writer.write(this.entityManager.getName(concept));
578
					writer.write(COMMA_SEPARATOR);
579
					writer.write(this.entityManager.getName(relation));
580
					writer.write(COMMA_SEPARATOR);
581
					writer.write(this.entityManager.getName(otherConcept));
582
					writer.newLine();
583
				}
584
			}
585
		}
586
		writer.flush();
587
	}
588
589
}
590