/**
* This file is part of Archiv-Editor.
*
* The software Archiv-Editor serves as a client user interface for working with
* the Person Data Repository. See: pdr.bbaw.de
*
* The software Archiv-Editor was developed at the Berlin-Brandenburg Academy
* of Sciences and Humanities, Jägerstr. 22/23, D-10117 Berlin.
* www.bbaw.de
*
* Copyright (C) 2010-2013 Berlin-Brandenburg Academy
* of Sciences and Humanities
*
* The software Archiv-Editor was developed by @author: Christoph Plutte.
*
* Archiv-Editor is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Archiv-Editor 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Archiv-Editor.
* If not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package org.bbaw.pdr.ae.view.markup.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import net.sf.vex.css.StyleSheet;
import net.sf.vex.css.StyleSheetReader;
import net.sf.vex.dom.Document;
import net.sf.vex.dom.DocumentReader;
import net.sf.vex.dom.DocumentWriter;
import net.sf.vex.dom.Element;
import net.sf.vex.dom.IWhitespacePolicy;
import net.sf.vex.dom.RootElement;
import net.sf.vex.swt.VexWidget;
import org.bbaw.pdr.ae.common.AEConstants;
import org.bbaw.pdr.ae.model.Aspect;
import org.bbaw.pdr.ae.model.TaggingRange;
import org.bbaw.pdr.ae.view.control.interfaces.IMarkupEditor;
import org.bbaw.pdr.ae.view.markup.web.internal.Activator;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.resource.FontDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.PlatformUI;
import org.xml.sax.SAXException;
/**
* The Class MarkupEditorStext.
* @author Christoph Plutte
*/
public class MarkupEditorVex implements IMarkupEditor
{
private StyleSheet styleSheet;
/** The _aspect. */
private Aspect _aspect;
/** The _composite. */
private Composite _composite;
/** The _title. */
private String _title;
/** The _selected tr. */
private TaggingRange[] _selectedTRs;
/** The _font descriptor. */
// Rap auskommentieren SWT.ROMAN
private FontDescriptor _fontDescriptor = FontDescriptor.createFrom("Tahoma", 10, 3); //$NON-NLS-1$
/** Resource Manager for colors and fonts. */
private LocalResourceManager _resources = new LocalResourceManager(JFaceResources.getResources());
/** The _markup listeners. */
private ArrayList<Listener> _markupListeners = new ArrayList<Listener>();
/** The _modify listeners. */
private ArrayList<Listener> _modifyListeners = new ArrayList<Listener>();
/** The _focus listeners. */
private ArrayList<Listener> _focusListeners = new ArrayList<Listener>();
/** The _key listeners. */
private ArrayList<Listener> _keyListeners = new ArrayList<Listener>();
/** The _text selection listeners. */
private ArrayList<Listener> _textSelectionListeners = new ArrayList<Listener>();
private HashMap<Element, TaggingRange> _taggingRangeMap;
private HashMap<TaggingRange, TaggingRange> _recentTaggingRangeMap;
private VexWidget _vexWidget;
@Override
public final void addExtendedModifyListener(final Listener extendedModifyListener)
{
if (extendedModifyListener != null)
{
_modifyListeners.add(extendedModifyListener);
}
}
@Override
public final void addFocusListener(final Listener focusListener)
{
if (focusListener != null)
{
_focusListeners.add(focusListener);
}
}
@Override
public final void addKeyListener(final Listener keyListener)
{
if (keyListener != null)
{
_keyListeners.add(keyListener);
}
}
@Override
public final void addMarkupSelectionListener(final Listener listener)
{
if (listener != null)
{
_markupListeners.add(listener);
}
}
@Override
public final void addTextSelectionListener(final Listener listener)
{
if (listener != null)
{
_textSelectionListeners.add(listener);
}
}
@Override
public final void createEditor()
{
_vexWidget = new VexWidget(_composite, SWT.MULTI | SWT.WRAP | SWT.V_SCROLL | SWT.BORDER);
GridData gd = new GridData();
gd.grabExcessHorizontalSpace = true;
gd.horizontalAlignment = SWT.FILL;
gd.grabExcessVerticalSpace = true;
gd.verticalAlignment = SWT.FILL;
// gd.widthHint = 280;
gd.heightHint = 50;
_vexWidget.setLayoutData(gd);
_vexWidget.setFont(_resources.createFont(_fontDescriptor)); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(_vexWidget, "org.bbaw.pdr.ae.help.notificationText"); //$NON-NLS-1$
_vexWidget.addSelectionChangedListener(new ISelectionChangedListener()
{
public void selectionChanged(SelectionChangedEvent event)
{
System.out.println("vexwidget selection changed");
// Integer n = null;
// System.out.println(n.toString());
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
Object[] objs = selection.toArray();
if (objs instanceof Object[])
{
ArrayList<TaggingRange> trs = new ArrayList<TaggingRange>(objs.length);
boolean textSelection = _vexWidget.getCurrentElement().getName().equals("notification");
for (int i = 0; i < objs.length; i++)
{
if (objs[i] instanceof Element)
{
Element el = (Element) objs[i];
if (!el.getName().equals("notification"))
{
textSelection = false;
trs.add(_taggingRangeMap.get(el));
}
}
}
if (textSelection)
{
_selectedTRs = null;
Event se = new Event();
se.data = _selectedTRs;
se.type = SWT.Selection;
for (Listener s : _markupListeners)
{
s.handleEvent(se);
}
Event see = new Event();
see.data = _vexWidget.getSelectedText();
for (Listener s : _textSelectionListeners)
{
s.handleEvent(see);
}
}
else
{
_selectedTRs = (TaggingRange[]) trs.toArray(new TaggingRange[trs.size()]);
Event se = new Event();
se.data = _selectedTRs;
se.type = SWT.Selection;
for (Listener s : _markupListeners)
{
s.handleEvent(se);
}
}
}
_vexWidget.layout();
_vexWidget.redraw();
_vexWidget.update();
}
});
final ControlDecoration decoNoti = new ControlDecoration(_vexWidget, SWT.LEFT | SWT.TOP);
_vexWidget.addFocusListener(new FocusListener()
{
@Override
public void focusLost(FocusEvent e)
{
saveChanges();
Event se = new Event();
se.doit = false;
for (Listener l : _focusListeners)
{
l.handleEvent(se);
}
}
@Override
public void focusGained(FocusEvent e)
{
Event se = new Event();
se.doit = true;
for (Listener l : _focusListeners)
{
l.handleEvent(se);
}
}
});
_vexWidget.addKeyListener(new KeyListener()
{
@Override
public void keyReleased(KeyEvent e)
{
Event se = new Event();
se.data = e.character;
for (Listener s : _modifyListeners)
{
s.handleEvent(se);
}
}
@Override
public void keyPressed(KeyEvent e)
{
// TODO Auto-generated method stub
}
});
}
private boolean compareTaggingRangeAndElement(TaggingRange tr, Element el)
{
if (tr.getName().equals(el.getName())
&& (tr.getType() == null || tr.getType().trim().length() == 0 || tr.getType().equals(
el.getAttribute("type")))
&& (tr.getSubtype() == null || tr.getSubtype().trim().length() == 0 || tr.getSubtype().equals(
el.getAttribute("subtype")))
&& (tr.getRole() == null || tr.getRole().trim().length() == 0 || tr.getRole().equals(
el.getAttribute("role")))
&& tr.getTextValue().equals(el.getText()))
{
System.out.println("tr equal el");
return true;
}
return false;
}
private static StyleSheet createStyleSheet()
{
final URL styleSheetUrl = MarkupEditorVex.class.getResource("comment.css");
final StyleSheetReader reader = new StyleSheetReader();
try
{
return reader.read(styleSheetUrl);
}
catch (IOException e)
{
AEConstants.ILOGGER.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Error trying to read Stylesheet: " + styleSheetUrl.toString()));
throw new AssertionError("Cannot read the stylesheet: " + e.getMessage());
}
}
@Override
public void saveChanges()
{
if (_aspect != null && _vexWidget != null && _vexWidget.getDocument() != null)
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
DocumentWriter docWriter = new DocumentWriter();
IWhitespacePolicy wsp = new IWhitespacePolicy()
{
@Override
public boolean isPre(Element element)
{
System.out.println("isPre " + element.getName());
if (element.getName().equals("notification"))
{
return true;
}
else
{
return true;
}
}
@Override
public boolean isBlock(Element element)
{
System.out.println("isBlock " + element.getName());
if (element.getName().equals("notification"))
{
return true;
}
else
{
return true;
}
}
};
docWriter.setWhitespacePolicy(wsp);
System.out.println("document encoding " + _vexWidget.getDocument().getEncoding());
try
{
docWriter.write(_vexWidget.getDocument(), output);
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
String xml = null;
try
{
xml = new String(output.toByteArray(), "UTF-8");
}
catch (UnsupportedEncodingException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
Pattern p = Pattern.compile("<\\?xml version='1.0'\\?>");
Matcher m = p.matcher(xml);
xml = m.replaceAll("<\\?xml version=\"1.0\" encoding=\"UTF-8\"\\?>");
System.out.println("saveChanges in vex " + xml);
_aspect.setNotificationAsXML(xml);
}
}
@Override
public final void deleteMarkup(final TaggingRange[] taggingRanges)
{
if (taggingRanges != null)
{
if (_recentTaggingRangeMap != null)
{
for (TaggingRange tr : taggingRanges)
{
_aspect.getRangeList().remove(_recentTaggingRangeMap.get(tr));
}
}
else
{
for (TaggingRange tr : taggingRanges)
{
_aspect.getRangeList().remove(tr);
}
}
int caret = _vexWidget.getCaretOffset();
try
{
loadValues();
}
catch (ParserConfigurationException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (SAXException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (XMLStreamException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
_vexWidget.moveTo(caret);
}
}
@Override
public final Aspect getAspect()
{
return _aspect;
}
@Override
public final Control getControl()
{
return _vexWidget;
}
@Override
public final TaggingRange[] getSelectedMarkups()
{
return _selectedTRs;
}
@Override
public final String getTitle()
{
return _title;
}
@Override
public final void insertContentSetMarkup(final TaggingRange taggingRange)
{
if (taggingRange.getType() != null && taggingRange.getType().trim().length() > 0
&& taggingRange.getTextValue().trim().length() > 0)
{
Collections.sort(_aspect.getRangeList());
int start = _vexWidget.getSelectionStart();
String selectedText = taggingRange.getTextValue();
taggingRange.setStart(start);
taggingRange.setLength(selectedText.length());
Element el = new Element(taggingRange.getName());
if (taggingRange.getType() != null && taggingRange.getType().trim().length() > 0)
{
el.setAttribute("type", taggingRange.getType());
}
if (taggingRange.getSubtype() != null && taggingRange.getSubtype().trim().length() > 0)
{
el.setAttribute("subtype", taggingRange.getSubtype());
}
if (taggingRange.getRole() != null && taggingRange.getRole().trim().length() > 0)
{
el.setAttribute("role", taggingRange.getRole());
}
if (taggingRange.getFrom() != null)
{
el.setAttribute("from", taggingRange.getFrom().toString());
}
if (taggingRange.getTo() != null)
{
el.setAttribute("to", taggingRange.getTo().toString());
}
if (taggingRange.getWhen() != null)
{
el.setAttribute("when", taggingRange.getWhen().toString());
}
if (taggingRange.getNotBefore() != null)
{
el.setAttribute("notBefore", taggingRange.getNotBefore().toString());
}
if (taggingRange.getNotAfter() != null)
{
el.setAttribute("notAfter", taggingRange.getNotAfter().toString());
}
if (taggingRange.getKey() != null && taggingRange.getKey().trim().length() > 0)
{
el.setAttribute("key", taggingRange.getKey());
}
if (taggingRange.getAna() != null && taggingRange.getAna().trim().length() > 0)
{
el.setAttribute("ana", taggingRange.getAna());
}
_taggingRangeMap.put(el, taggingRange);
_vexWidget.insertText(selectedText);
_vexWidget.moveTo(start, false);
_vexWidget.moveTo(start + selectedText.length(), true);
_vexWidget.insertElement(el);
saveChanges();
loadTaggingMap();
}
}
@Override
public final boolean isValid()
{
return true;
}
/**
* Load values.
* @throws XMLStreamException
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
*/
private void loadValues() throws ParserConfigurationException, SAXException, IOException, XMLStreamException
{
System.out.println("loadValues");
styleSheet = createStyleSheet();
_taggingRangeMap = new HashMap<Element, TaggingRange>();
Document document = createNotificatinDoc();
System.out.println("load values encoding " + document.getEncoding());
// document.setEncoding("UTF-8");
// _vexWidget.setForeground(AEVIEWConstants.VIEW_FOREGROUND_SELECTED_COLOR);
_vexWidget.setDocument(document, styleSheet);
loadTaggingMap();
// _vexWidget.setStyleSheet(styleSheet);
// _vexWidget.redraw();
_vexWidget.layout();
_vexWidget.redraw();
_vexWidget.update();
}
private void loadTaggingMap()
{
// Tagging
LinkedList<TaggingRange> rangeList;
ArrayList<Element> processedElements = new ArrayList<Element>();
if (_aspect.getRangeList() != null)
{
rangeList = (LinkedList<TaggingRange>) _aspect.getRangeList();
for (int i = 1; i < _vexWidget.getDocument().getLength(); i++)
{
Element e = _vexWidget.getDocument().getElementAt(i);
if (e != null && !processedElements.contains(e))
{
processedElements.add(e);
for (TaggingRange tr : rangeList)
{
if (compareTaggingRangeAndElement(tr, e))
{
_taggingRangeMap.put(e, tr);
}
}
}
}
}
}
private Document createNotificatinDoc() throws ParserConfigurationException, SAXException, IOException,
XMLStreamException
{
Document document;
if (_aspect.getNotification() != null)
{
document = new DocumentReader().read(replaceLB(_aspect.getNotificationAsXML()));
// document.setValidator(createValidator());
return document;
}
else
{
document = new Document(new RootElement("notification"));
// document.setValidator(createValidator());
}
return document;
}
private String replaceLB(String notificationAsXML)
{
notificationAsXML.replaceAll("<lb></lb>", "\n");
Pattern p = Pattern.compile("<lb></lb>");
Matcher m = p.matcher(notificationAsXML);
String replaced = m.replaceAll("\n");
return replaced;
}
@Override
public final void setAspect(final Aspect aspect)
{
this._aspect = aspect;
if (_aspect != null)
{
try
{
loadValues();
}
catch (ParserConfigurationException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (SAXException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (XMLStreamException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public final void setComposite(final Composite composite)
{
this._composite = composite;
}
@Override
public final void setEditable(final boolean editable)
{
_vexWidget.setEnabled(editable);
}
@Override
public final void setFont(final FontDescriptor fontDescriptor)
{
this._fontDescriptor = fontDescriptor;
}
@Override
public final void setMarkup(final TaggingRange taggingRange)
{
int start = _vexWidget.getSelectionStart();
int end = _vexWidget.getSelectionEnd();
String selectedText = _vexWidget.getSelectedText();
while (selectedText.startsWith(" "))
{
selectedText = selectedText.substring(1);
start++;
}
while (selectedText.endsWith(" "))
{
selectedText = selectedText.substring(0, selectedText.length() - 1);
end--;
}
taggingRange.setStart(start);
taggingRange.setLength(end - start + 1);
taggingRange.setTextValue(_vexWidget.getSelectedText());
Element el = new Element(taggingRange.getName());
if (taggingRange.getType() != null && taggingRange.getType().trim().length() > 0)
{
el.setAttribute("type", taggingRange.getType());
}
if (taggingRange.getSubtype() != null && taggingRange.getSubtype().trim().length() > 0)
{
el.setAttribute("subtype", taggingRange.getSubtype());
}
if (taggingRange.getRole() != null && taggingRange.getRole().trim().length() > 0)
{
el.setAttribute("role", taggingRange.getRole());
}
if (taggingRange.getFrom() != null)
{
el.setAttribute("from", taggingRange.getFrom().toString());
}
if (taggingRange.getTo() != null)
{
el.setAttribute("to", taggingRange.getTo().toString());
}
if (taggingRange.getWhen() != null)
{
el.setAttribute("when", taggingRange.getWhen().toString());
}
if (taggingRange.getNotBefore() != null)
{
el.setAttribute("notBefore", taggingRange.getNotBefore().toString());
}
if (taggingRange.getNotAfter() != null)
{
el.setAttribute("notAfter", taggingRange.getNotAfter().toString());
}
if (taggingRange.getKey() != null && taggingRange.getKey().trim().length() > 0)
{
el.setAttribute("key", taggingRange.getKey());
}
if (taggingRange.getAna() != null && taggingRange.getAna().trim().length() > 0)
{
el.setAttribute("ana", taggingRange.getAna());
}
_taggingRangeMap.put(el, taggingRange);
_vexWidget.insertElement(el);
saveChanges();
loadTaggingMap();
}
@Override
public final void setTitle(final String title)
{
this._title = title;
}
@Override
public void refresh()
{
if (_aspect != null && _vexWidget != null)
{
int offset = _vexWidget.getCaretOffset();
int textLength = _vexWidget.getDocument().getLength();
int rangeNum = _vexWidget.getDocument().getRootElement().getChildNodes().length;
try
{
loadValues();
}
catch (ParserConfigurationException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (SAXException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (XMLStreamException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
_vexWidget.moveTo(offset);
if (textLength != _vexWidget.getDocument().getLength()
|| rangeNum != _vexWidget.getDocument().getRootElement().getChildNodes().length)
{
Event se = new Event();
for (Listener s : _modifyListeners)
{
s.handleEvent(se);
}
}
}
}
@Override
public void insert(String string)
{
_vexWidget.insertText(string);
}
@Override
public void setSelected(boolean selected)
{
// if (selected)
// {
// _vexWidget.setForeground(AEVIEWConstants.VIEW_FOREGROUND_SELECTED_COLOR);
// }
// else
// {
// _vexWidget.setForeground(AEVIEWConstants.VIEW_FOREGROUND_DESELECTED_COLOR);
// }
// _vexWidget.setStyleSheet(styleSheet);
}
@Override
public void setFocus()
{
_vexWidget.setFocus();
}
@Override
public String getSelectionText()
{
return _vexWidget.getSelectedText();
}
}