[LtxR-Model, WikidocR-Model] Make RChunkReconciler fully thread safe

Change-Id: I5228e7f1eaeeac88e42ea7340d18b2b3d2dc76cf
diff --git a/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/core/model/RChunkReconciler.java b/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/core/model/RChunkReconciler.java
index 3992ca9..962581d 100644
--- a/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/core/model/RChunkReconciler.java
+++ b/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/core/model/RChunkReconciler.java
@@ -67,14 +67,15 @@
 	
 	private final RModelManager rManager;
 	
+	private final Object raLock= new Object();
 	private final Matcher raChunkStartLineMatcher;
 	private final @Nullable Matcher raChunkRefLineMatcher;
 	private final Matcher raChunkEndLineMatcher;
-	
 	private final RParser raParser= new RParser(RSourceConfig.DEFAULT_CONFIG,
 			AstInfo.LEVEL_MODEL_DEFAULT, new CacheStringFactory(0x20) );
 	private final StringParserInput raInput= new StringParserInput(0x1000);
 	
+	private final Object riLock= new Object();
 	private final RIssueReporter riReporter= new RIssueReporter();
 	
 	
@@ -100,112 +101,114 @@
 	public void reconcileAst(final TModelContainer adapter,
 			final SourceContent sourceContent, final List<? extends EmbeddingAstNode> list,
 			final RSourceConfig rSourceConfig) {
-		if (sourceContent.getStartOffset() != 0) {
-			throw new UnsupportedOperationException();
-		}
-		
-		final String source= sourceContent.getString();
-		final TextLineInformation lines= sourceContent.getStringLines();
-		
-		this.raChunkStartLineMatcher.reset(source);
-		if (this.raChunkRefLineMatcher != null) {
-			this.raChunkRefLineMatcher.reset(source);
-		}
-		this.raChunkEndLineMatcher.reset(source);
-		
-		this.raInput.reset(source);
-		
-		this.raParser.setRSourceConfig(rSourceConfig);
-		this.raParser.setCommentLevel(RParser.COLLECT_COMMENTS | RParser.ROXYGEN_COMMENTS);
-		
-		for (final EmbeddingAstNode embeddingNode : list) {
-			if (embeddingNode.getForeignTypeId() != RModel.R_TYPE_ID) {
-				continue;
+		synchronized (this.raLock) {
+			if (sourceContent.getStartOffset() != 0) {
+				throw new UnsupportedOperationException();
 			}
 			
-			final RChunkNode rChunk= new RChunkNode(embeddingNode);
-			rChunk.startOffset= embeddingNode.getStartOffset();
-			rChunk.endOffset= embeddingNode.getEndOffset();
-			embeddingNode.setForeignNode(rChunk);
+			final String source= sourceContent.getString();
+			final TextLineInformation lines= sourceContent.getStringLines();
 			
-			final IRegion startRegion;
-			final List<IRegion> rCode= new ArrayList<>(4);
+			this.raChunkStartLineMatcher.reset(source);
+			if (this.raChunkRefLineMatcher != null) {
+				this.raChunkRefLineMatcher.reset(source);
+			}
+			this.raChunkEndLineMatcher.reset(source);
 			
-			switch ((embeddingNode.getEmbedDescr() & 0xf)) {
+			this.raInput.reset(source);
 			
-			case EmbeddingAstNode.EMBED_INLINE:
-				startRegion= null;
-				rCode.add(new Region(embeddingNode.getStartOffset(), embeddingNode.getLength()));
-				break;
-				
-			case EmbeddingAstNode.EMBED_CHUNK: {
-				int lineOffset= rChunk.startOffset;
-				int line= lines.getLineOfOffset(rChunk.startOffset);
-				int rCodeStartOffset;
-				
-				// start line
-				int lineEndOffset= lines.getEndOffset(line);
-				this.raChunkStartLineMatcher.region(lineOffset, lineEndOffset);
-				if (!this.raChunkStartLineMatcher.matches()) {
-					throw new IllegalStateException("R chunk does not start with start line.");
+			this.raParser.setRSourceConfig(rSourceConfig);
+			this.raParser.setCommentLevel(RParser.COLLECT_COMMENTS | RParser.ROXYGEN_COMMENTS);
+			
+			for (final EmbeddingAstNode embeddingNode : list) {
+				if (embeddingNode.getForeignTypeId() != RModel.R_TYPE_ID) {
+					continue;
 				}
-				{	final int start= this.raChunkStartLineMatcher.start(1);
-					final int end= this.raChunkStartLineMatcher.end(1);
-					startRegion= new Region(start, end - start);
-				}
-				rCodeStartOffset= lineEndOffset;
 				
-				if (lineEndOffset < rChunk.endOffset) {
-					do {
-						line++;
-						lineOffset= lineEndOffset;
-						lineEndOffset= lines.getEndOffset(line);
-						
-						if (this.raChunkRefLineMatcher != null) {
-							this.raChunkRefLineMatcher.region(lineOffset, lineEndOffset);
-							if (this.raChunkRefLineMatcher.matches()) {
-								if (rCodeStartOffset < lineOffset) {
-									rCode.add(new Region(rCodeStartOffset, lineOffset - rCodeStartOffset));
+				final RChunkNode rChunk= new RChunkNode(embeddingNode);
+				rChunk.startOffset= embeddingNode.getStartOffset();
+				rChunk.endOffset= embeddingNode.getEndOffset();
+				embeddingNode.setForeignNode(rChunk);
+				
+				final IRegion startRegion;
+				final List<IRegion> rCode= new ArrayList<>(4);
+				
+				switch ((embeddingNode.getEmbedDescr() & 0xf)) {
+				
+				case EmbeddingAstNode.EMBED_INLINE:
+					startRegion= null;
+					rCode.add(new Region(embeddingNode.getStartOffset(), embeddingNode.getLength()));
+					break;
+					
+				case EmbeddingAstNode.EMBED_CHUNK: {
+					int lineOffset= rChunk.startOffset;
+					int line= lines.getLineOfOffset(rChunk.startOffset);
+					int rCodeStartOffset;
+					
+					// start line
+					int lineEndOffset= lines.getEndOffset(line);
+					this.raChunkStartLineMatcher.region(lineOffset, lineEndOffset);
+					if (!this.raChunkStartLineMatcher.matches()) {
+						throw new IllegalStateException("R chunk does not start with start line.");
+					}
+					{	final int start= this.raChunkStartLineMatcher.start(1);
+						final int end= this.raChunkStartLineMatcher.end(1);
+						startRegion= new Region(start, end - start);
+					}
+					rCodeStartOffset= lineEndOffset;
+					
+					if (lineEndOffset < rChunk.endOffset) {
+						do {
+							line++;
+							lineOffset= lineEndOffset;
+							lineEndOffset= lines.getEndOffset(line);
+							
+							if (this.raChunkRefLineMatcher != null) {
+								this.raChunkRefLineMatcher.region(lineOffset, lineEndOffset);
+								if (this.raChunkRefLineMatcher.matches()) {
+									if (rCodeStartOffset < lineOffset) {
+										rCode.add(new Region(rCodeStartOffset, lineOffset - rCodeStartOffset));
+									}
+									rCodeStartOffset= lineEndOffset;
 								}
-								rCodeStartOffset= lineEndOffset;
 							}
+						} while (lineEndOffset < rChunk.endOffset);
+						
+						if (rChunk.endOffset != lineEndOffset) {
+							throw new IllegalStateException("R chunk does not end at line end.");
 						}
-					} while (lineEndOffset < rChunk.endOffset);
-					
-					if (rChunk.endOffset != lineEndOffset) {
-						throw new IllegalStateException("R chunk does not end at line end.");
-					}
-					
-					this.raChunkEndLineMatcher.region(lineOffset, lineEndOffset);
-					if (this.raChunkEndLineMatcher.matches()) {
-						if (rCodeStartOffset < lineOffset) {
-							rCode.add(new Region(rCodeStartOffset, lineOffset - rCodeStartOffset));
+						
+						this.raChunkEndLineMatcher.region(lineOffset, lineEndOffset);
+						if (this.raChunkEndLineMatcher.matches()) {
+							if (rCodeStartOffset < lineOffset) {
+								rCode.add(new Region(rCodeStartOffset, lineOffset - rCodeStartOffset));
+							}
+							rCodeStartOffset= lineEndOffset;
 						}
-						rCodeStartOffset= lineEndOffset;
+						else if (rCodeStartOffset < lineOffset) {
+							rCode.add(new Region(rCodeStartOffset, lineEndOffset - rCodeStartOffset));
+						}
 					}
-					else if (rCodeStartOffset < lineOffset) {
-						rCode.add(new Region(rCodeStartOffset, lineEndOffset - rCodeStartOffset));
-					}
+					break; }
+				
+				default:
+					throw new UnsupportedOperationException("embedType= " + embeddingNode.getEmbedDescr());
 				}
-				break; }
-			
-			default:
-				throw new UnsupportedOperationException("embedType= " + embeddingNode.getEmbedDescr());
+				
+				if (startRegion != null) {
+					rChunk.weaveArgs= this.raParser.scanFCallArgs(
+							this.raInput.init(startRegion.getOffset(), startRegion.getOffset() + startRegion.getLength()),
+							true );
+				}
+				final SourceComponent[] rCodeNodes= new @NonNull SourceComponent[rCode.size()];
+				for (int j= 0; j < rCodeNodes.length; j++) {
+					final IRegion region= rCode.get(j);
+					rCodeNodes[j]= this.raParser.scanSourceRange(
+							this.raInput.init(region.getOffset(), region.getOffset() + region.getLength()),
+							rChunk, true );
+				}
+				rChunk.rSources= ImCollections.newList(rCodeNodes);
 			}
-			
-			if (startRegion != null) {
-				rChunk.weaveArgs= this.raParser.scanFCallArgs(
-						this.raInput.init(startRegion.getOffset(), startRegion.getOffset() + startRegion.getLength()),
-						true );
-			}
-			final SourceComponent[] rCodeNodes= new @NonNull SourceComponent[rCode.size()];
-			for (int j= 0; j < rCodeNodes.length; j++) {
-				final IRegion region= rCode.get(j);
-				rCodeNodes[j]= this.raParser.scanSourceRange(
-						this.raInput.init(region.getOffset(), region.getOffset() + region.getLength()),
-						rChunk, true );
-			}
-			rChunk.rSources= ImCollections.newList(rCodeNodes);
 		}
 	}
 	
@@ -307,8 +310,10 @@
 		if (modelInfo == null) {
 			return;
 		}
-		this.riReporter.configure(config.getIssueConfig());
-		this.riReporter.run(adapter.getSourceUnit(), modelInfo, content, issueRequestor, level);
+		synchronized (this.riLock) {
+			this.riReporter.configure(config.getIssueConfig());
+			this.riReporter.run(adapter.getSourceUnit(), modelInfo, content, issueRequestor, level);
+		}
 	}
 	
 }