/* license-start
*
* Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, at <http://www.gnu.org/licenses/>.
*
* Contributors:
* Crispico - Initial API and implementation
*
* license-end
*/
package com.crispico.flower.mp.codesync.wiki;
import static com.crispico.flower.mp.codesync.wiki.WikiPlugin.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.flowerplatform.common.regex.RegexProcessingSession;
import org.flowerplatform.model.astcache.wiki.AstCacheWikiFactory;
import org.flowerplatform.model.astcache.wiki.FlowerBlock;
import org.flowerplatform.model.astcache.wiki.NodeWithOriginalFormat;
import com.crispico.flower.mp.model.codesync.CodeSyncElement;
import com.crispico.flower.mp.model.codesync.CodeSyncFactory;
import com.crispico.flower.mp.model.codesync.CodeSyncPackage;
/**
* @author Mariana
*/
public class WikiTreeBuilder extends RegexProcessingSession {
/**
* Used for final levels (e.g. paragraphs).
*/
protected final int LEAF_LEVEL = 100;
protected CodeSyncElement root;
protected List<FlowerBlock> flowerBlocks;
protected Resource resource;
protected CodeSyncElement currentNode;
protected IDocument document;
public void setRoot(CodeSyncElement root) {
if (root == null)
throw new IllegalArgumentException("Root for session cannot be null");
this.root = root;
this.currentNode = root;
for (Iterator it = root.getChildren().iterator(); it.hasNext();) {
CodeSyncElement child = (CodeSyncElement) it.next();
if (!PAGE_CATEGORY.equals(child.getType()) && !FOLDER_CATEGORY.equals(child.getType())) {
it.remove();
}
}
}
public CodeSyncElement getRoot() {
return root;
}
public void setInput(String input) {
document = new Document(input);
}
public void setResource(Resource resource) {
this.resource = resource;
}
/**
* If not <code>null</code>, the content of a new {@link FlowerBlockNode} will be retrieved
* from this list, if there is a {@link FlowerBlockNode} with the same id.
*/
public void setFlowerBlocks(List<FlowerBlock> flowerBlocks) {
this.flowerBlocks = flowerBlocks;
if (flowerBlocks == null) {
this.flowerBlocks = new ArrayList<FlowerBlock>();
}
}
@Override
public void candidateAnnounced(String category) {
super.candidateAnnounced(category);
CodeSyncElement node = createWikiNode(category);
while (!acceptAsChild(node)) {
currentNode = getParent(currentNode);
}
currentNode.getChildren().add(node);
currentNode = node;
}
private CodeSyncElement getParent(CodeSyncElement node) {
EObject eObj = node.eContainer();
if (eObj instanceof CodeSyncElement) {
return (CodeSyncElement) eObj;
}
return null;
}
/**
* Creates and populates a {@link CodeSyncElement} for the matched <code>category</code>.
*/
protected CodeSyncElement createWikiNode(String category) {
if (FLOWER_BLOCK_CATEGORY.equals(category)) {
return createFlowerBlockNode();
}
if (ORDERED_LIST_ITEM_CATEGORY.equals(category) || UNORDERED_LIST_ITEM_CATEGORY.equals(category)) {
return createListItemNode(category);
}
if (BLOCKQUOTE_CHILD_CATEGORY.equals(category)) {
return createBlockquoteChildNode(category);
}
if (CODE_LINE_CATEGORY.equals(category)) {
return createCodeLineNode(category);
}
if (ORDERED_LIST_CATEGORY.equals(category) || UNORDERED_LIST_CATEGORY.equals(category) ||
BLOCKQUOTE_CATEGORY.equals(category) ||
CODE_CATEGORY.equals(category)) {
return createParentNode(category);
}
if (WikiPlugin.getInstance().getHeadingLevel(category) > 0) {
return createNodeWithOriginalFormat(category);
}
CodeSyncElement cse = CodeSyncPackage.eINSTANCE.getCodeSyncFactory().createCodeSyncElement();
cse.setName(currentSubMatchesForCurrentRegex[0]);
cse.setType(category);
return cse;
}
/**
* If this is the first list item, create the parent list node.
*/
protected CodeSyncElement createListItemNode(String category) {
CodeSyncElement item = createNodeWithOriginalFormat(category);
if (!ORDERED_LIST_ITEM_CATEGORY.equals(currentNode.getType()) && !UNORDERED_LIST_ITEM_CATEGORY.equals(currentNode.getType())) {
candidateAnnounced(ORDERED_LIST_ITEM_CATEGORY.equals(category) ? ORDERED_LIST_CATEGORY : UNORDERED_LIST_CATEGORY);
}
return item;
}
/**
* If this is the first blockquote child, create the parent blockquote node.
*/
protected CodeSyncElement createBlockquoteChildNode(String category) {
CodeSyncElement child = createNodeWithOriginalFormat(category);
if (!BLOCKQUOTE_CHILD_CATEGORY.equals(currentNode.getType())) {
candidateAnnounced(BLOCKQUOTE_CATEGORY);
}
return child;
}
/**
* If this is the first code line, create the parent code node.
*/
protected CodeSyncElement createCodeLineNode(String category) {
CodeSyncElement line = createNodeWithOriginalFormat(category);
if (!CODE_LINE_CATEGORY.equals(currentNode.getType())) {
candidateAnnounced(CODE_CATEGORY);
}
return line;
}
protected CodeSyncElement createParentNode(String category) {
CodeSyncElement parent = CodeSyncFactory.eINSTANCE.createCodeSyncElement();
parent.setName(category);
parent.setType(category);
return parent;
}
protected CodeSyncElement createNodeWithOriginalFormat(String category) {
NodeWithOriginalFormat node = AstCacheWikiFactory.eINSTANCE.createNodeWithOriginalFormat();
String originalFormat = currentSubMatchesForCurrentRegex[0];
String name = currentSubMatchesForCurrentRegex[1];
StringBuilder builder = new StringBuilder(originalFormat);
int index = builder.lastIndexOf(name);
builder.replace(index, index + name.length(), "%s");
node.setOriginalFormat(builder.toString());
node.setName(name);
node.setType(category);
return node;
}
/**
* Retrieves the content of the new {@link FlowerBlockNode} from the {@link #flowerBlocks} list,
* if there is an existing block with the same id. Otherwise, creates the content from the current
* match.
*/
private CodeSyncElement createFlowerBlockNode() {
int lineStart = -1, lineEnd = -1;
try {
lineStart = document.getLineOfOffset(matcher.start());
lineEnd = document.getLineOfOffset(matcher.end()) + 1;
} catch (Exception e) {
}
String name = currentSubMatchesForCurrentRegex[0];
String content = currentSubMatchesForCurrentRegex[1];
for (FlowerBlock block : flowerBlocks) {
if (block.getCodeSyncElement().getName().equals(name)) {
content = block.getContent();
break;
}
}
CodeSyncElement cse = CodeSyncPackage.eINSTANCE.getCodeSyncFactory().createCodeSyncElement();
FlowerBlock flowerBlock = AstCacheWikiFactory.eINSTANCE.createFlowerBlock();
cse.setName(name);
cse.setType(FLOWER_BLOCK_CATEGORY);
flowerBlock.setContent(content);
flowerBlock.setLineStart(lineStart);
flowerBlock.setLineEnd(lineEnd);
cse.setAstCacheElement(flowerBlock);
if (resource != null) {
resource.getContents().add(flowerBlock);
}
return cse;
}
/**
* @return true if {@link #currentNode} can accept <code>candidate</code> as a child,
* false otherwise
*/
protected boolean acceptAsChild(CodeSyncElement candidate) {
Level currentLevel = getLevelForCategory(currentNode.getType());
Level candidateLevel = getLevelForCategory(candidate.getType());
return currentLevel.acceptChild(candidateLevel);
}
/**
* @return the level of indentation for this category (e.g. heading level). Nodes with lower
* level will contain nodes with higher levels.
*/
protected Level getLevelForCategory(String category) {
if (PARAGRAPH_CATEGORY.equals(category)) {
return Level.PARAGRAPH; // cannot have children
}
if (ORDERED_LIST_ITEM_CATEGORY.equals(category) || UNORDERED_LIST_ITEM_CATEGORY.equals(category)) {
return Level.LIST_ITEM; // cannot have children
}
if (ORDERED_LIST_CATEGORY.equals(category) || UNORDERED_LIST_CATEGORY.equals(category)) {
return Level.LIST;
}
if (BLOCKQUOTE_CHILD_CATEGORY.equals(category)) {
return Level.BLOCKQUOTE_CHILD; // cannot have children
}
if (BLOCKQUOTE_CATEGORY.equals(category)) {
return Level.BLOCKQUOTE;
}
if (CODE_LINE_CATEGORY.equals(category)) {
return Level.CODE_LINE;
}
if (CODE_CATEGORY.equals(category)) {
return Level.CODE;
}
int headingLevel = WikiPlugin.getInstance().getHeadingLevel(category);
if (headingLevel > 0) {
return Level.getHeading(headingLevel);
}
if (FOLDER_CATEGORY.equals(category)) {
return Level.FOLDER;
}
if (PAGE_CATEGORY.equals(category)) {
return Level.FILE; // page can contain anything that is not a folder
}
if (FLOWER_BLOCK_CATEGORY.equals(category)) {
return Level.FLOWER_BLOCK; // cannot have children
}
throw new RuntimeException("Unknown category " + category);
}
}