/******************************************************************************* * Copyright (c) 2011 Sebastian Benz. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Sebastian Benz - initial API and implementation ******************************************************************************/ package de.sebastianbenz.task.impl; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Maps.newTreeMap; import static java.util.regex.Pattern.compile; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.util.EObjectContainmentEList; import org.eclipse.emf.ecore.util.EObjectResolvingEList; import de.sebastianbenz.task.Container; import de.sebastianbenz.task.Content; import de.sebastianbenz.task.EmptyLine; import de.sebastianbenz.task.Image; import de.sebastianbenz.task.Link; import de.sebastianbenz.task.Tag; import de.sebastianbenz.task.TaskFactory; import de.sebastianbenz.task.TaskModel; import de.sebastianbenz.task.TaskPackage; import de.sebastianbenz.task.Text; import de.sebastianbenz.task.TextSegment; import de.sebastianbenz.task.tagging.Tags; import de.sebastianbenz.task.util.Images; import de.sebastianbenz.task.util.Links; public class ContentImplCustom extends de.sebastianbenz.task.impl.ContentImpl { private static final Pattern INTEND = Pattern.compile(" {2}|\t"); public static final String DONE_TAG = "done"; private int level = -1; @Override public int getLevel() { if(level == -1){ level = 0; Matcher matcher = INTEND.matcher(getIntend()); while(matcher.find()){ level++; } } return level ; } @Override public String getIntend() { String result = super.getIntend(); if(result == null){ result = ""; } return result; } private static final int SPACE = 1; private enum DoneStatus { UNKNOWN, OPEN, COMPLETED } public static final String TAG = "(^|\\W)@([\\w������]+)(\\((.*?)\\))?"; private static final String URL = "[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|]"; private static final Pattern TAG_PATTERN = Pattern.compile(TAG, Pattern.DOTALL); private static final Pattern URL_DESCRIPTION_PATTERN = compile("(!)?\\[([-A-Za-z0-9+&@#/%?=~_()|!:,.;\\s]+)\\]\\((" + URL + ")\\)|\\(?\\b(http://|www[.])" + URL); private DoneStatus isDone = DoneStatus.UNKNOWN; private String value; @Override public EList<Tag> getTags() { if (tags == null) { tags = new EObjectContainmentEList<Tag>(Tag.class, this, TaskPackage.TASK__TAGS); parseTags(); } return tags; } @Override public Container getParent() { if (parent == null) { setParent(resolveContainer()); } return parent; } protected Container resolveContainer() { TaskModel taskModel = getTaskModel(); if(taskModel == null){ return null; } EList<Content> allContents = taskModel.getContents(); int index = allContents.indexOf(this); if(index == 0){ return taskModel; } for (int i = index - 1; i >= 0; i--) { Content candidate = allContents.get(i); if (!(candidate instanceof EmptyLine) && candidate.getLevel() < getLevel()) { return (Container) candidate; } } return taskModel; } @Override public boolean isDone() { if (isDone == DoneStatus.UNKNOWN) { isDone = resolveStatus(); } return isDone == DoneStatus.COMPLETED; } protected DoneStatus resolveStatus() { for (Tag tag : getTags()) { if (tag.getName().equals(DONE_TAG)) { return DoneStatus.COMPLETED; } } if (getParent() instanceof Content) { Content parent = (Content) getParent(); if (parent.isDone()) { return DoneStatus.COMPLETED; } } return DoneStatus.OPEN; } @Override public String getValue() { if(value == null){ if(getText() == null){ value = ""; }else{ value = removeTags(getText()); value = cleanText(value); } } return value; } @Override public void setText(String newText) { tags = null; isDone = DoneStatus.UNKNOWN; value = null; super.setText(newText); } protected String removeTags(String string) { return string.replaceAll(TAG, "").trim().replaceAll(" ", " "); } protected String cleanText(String text){ return text; } @Override public EList<Link> getLinks() { if(links == null){ parseLinksAndImages(); } return links; } protected void parseTags() { if(text == null){ return; } Matcher matcher = TAG_PATTERN.matcher(text); while (matcher.find()) { String name = matcher.group(2); String value = matcher.group(4); int offset = matcher.start() + SPACE; int length = matcher.end() - offset; getTags().add(Tags.create(name, value, offset, length)); } } @Override public EList<Image> getImages() { if(images == null){ parseLinksAndImages(); } return images; } private void parseLinksAndImages() { links = new EObjectContainmentEList<Link>(Link.class, this, TaskPackage.CONTENT__LINKS); images = new EObjectContainmentEList<Image>(Image.class, this, TaskPackage.CONTENT__IMAGES); if (text == null) { return; } Matcher matcher = URL_DESCRIPTION_PATTERN.matcher(text); while (matcher.find()) { int offset = matcher.start(); int length = matcher.end() - offset; String description = ""; String url; if(isImage(matcher)){ description = matcher.group(2); url = matcher.group(3); images.add(Images.create(description, url, offset, length)); }else if(isLinkWithDescription(matcher)){ description = matcher.group(2); url = matcher.group(3); links.add(Links.create(description, url, offset, length)); }else{ url = matcher.group(); links.add(Links.create(description, url, offset, length)); } } } private boolean isImage(Matcher matcher) { return matcher.group(1) != null; } private boolean isLinkWithDescription(Matcher matcher) { return matcher.group(2) != null && matcher.group(3) != null; } @Override public EList<TextSegment> getSegments() { if(segments == null){ segments = new EObjectResolvingEList<TextSegment>(TextSegment.class, this, TaskPackage.CONTENT__SEGMENTS); calculateSegments(segments); } return super.getSegments(); } private void calculateSegments(EList<TextSegment> segments) { TreeMap<Integer, TextSegment> offsets = newTreeMap(); add(offsets, concat(getLinks(), getTags(), getImages())); if(offsets.isEmpty()){ addTextSegment(segments, 0, getText().length()); return; } int begin = 0; for (TextSegment segment : offsets.values()) { int newOffset = segment.getOffset(); if(newOffset > begin){ addTextSegment(segments, begin, newOffset); } segments.add(segment); begin = newOffset + segment.getLength(); } addTextSegment(segments, begin, getText().length()); } protected void addTextSegment(EList<TextSegment> segments, int begin, int end) { if(begin >= end){ return; } if(begin == end){ return; } Text text = TaskFactory.eINSTANCE.createText(); int textLength = end - begin; text.setOffset(begin); text.setLength(textLength); text.setValue(getText().substring(begin, begin + textLength)); segments.add(text); } private void add(TreeMap<Integer, TextSegment> offsets, Iterable<? extends TextSegment> segments) { for (TextSegment s : segments) { offsets.put(s.getOffset(), s); } } }