Commit 6d668158 authored by Johannes Roith's avatar Johannes Roith

Merge branch 'master' of github.com:jroith/cup-eclipse

parents 86377c0d 6c8da780
......@@ -2,14 +2,12 @@ package de.tum.in.www2.cupplugin;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
import org.eclipse.core.resources.IWorkspace;
import de.tum.in.www2.cupplugin.debug.BreakpointMapper;
import de.tum.in.www2.cupplugin.debug.Debugger;
/**
......@@ -36,8 +34,8 @@ public class Activator extends AbstractUIPlugin {
*/
public Activator() {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
workspace.addResourceChangeListener(new DebugListener(),
IResourceChangeEvent.POST_BUILD);
workspace.addResourceChangeListener(new DebugListener(),
IResourceChangeEvent.POST_BUILD);
}
/*
......
......@@ -4,44 +4,40 @@ import org.eclipse.swt.graphics.RGB;
public class Colors {
public static final RGB black = new RGB (0, 0, 0);
public static final RGB white = new RGB (255, 255, 255);
public static final RGB darkGray = new RGB (20, 20, 20);
// public static final RGB darkGray = new RGB (30, 30, 30);
public static final RGB gray = new RGB (150, 150, 150);
public static final RGB lightGray = new RGB (240, 240, 240);
public static final RGB blue = new RGB (53, 87, 197);
public static final RGB brightBlue = new RGB (38, 99, 236);
public static final RGB green = new RGB (44, 173, 57);
public static final RGB orange = new RGB (220, 150, 50);
public static final RGB purple = new RGB (177, 33, 114);
public static final RGB blueGreen = new RGB (43, 126, 136);
public static final RGB red = new RGB (255, 0, 0);
public static final RGB pink = new RGB (255, 0, 255);
public static final RGB brown = new RGB (132, 90, 49);
public static final RGB black = new RGB(0, 0, 0);
public static final RGB white = new RGB(255, 255, 255);
public static final RGB darkGray = new RGB(20, 20, 20);
// public static final RGB darkGray = new RGB (30, 30, 30);
public static final RGB gray = new RGB(150, 150, 150);
public static final RGB lightGray = new RGB(240, 240, 240);
public static final RGB blue = new RGB(53, 87, 197);
public static final RGB brightBlue = new RGB(38, 99, 236);
public static final RGB green = new RGB(44, 173, 57);
public static final RGB orange = new RGB(220, 150, 50);
public static final RGB purple = new RGB(177, 33, 114);
public static final RGB blueGreen = new RGB(43, 126, 136);
public static final RGB red = new RGB(255, 0, 0);
public static final RGB pink = new RGB(255, 0, 255);
public static final RGB brown = new RGB(132, 90, 49);
/*
* public static final RGB stateGreen = new RGB (155, 218, 158); public
* static final RGB stateBlue = new RGB (155, 208, 128); public static final
* RGB stateRed = new RGB (218, 155, 173); public static final RGB
* stateYellow = new RGB (218, 214, 155);
*/
// public static final RGB stateGreen = new RGB (196, 232, 197);
public static final RGB stateGreen = new RGB(146, 203, 135);
public static final RGB stateGreen = new RGB (155, 218, 158);
public static final RGB stateBlue = new RGB (155, 208, 128);
public static final RGB stateRed = new RGB (218, 155, 173);
public static final RGB stateYellow = new RGB (218, 214, 155);
*/
//public static final RGB stateGreen = new RGB (196, 232, 197);
public static final RGB stateGreen = new RGB (146, 203, 135);
public static final RGB stateBlue = new RGB (196, 232, 230);
public static final RGB stateRed = new RGB (232, 196, 196);
public static final RGB stateYellow = new RGB (218, 214, 155);
public static final RGB stateBrightYellow = new RGB (241, 237, 122);
public static final RGB conflictBgBlue = new RGB (231, 244, 247);
public static final RGB conflictFgBlue = new RGB (17, 62, 91);
public static final RGB conflictBgYellow = new RGB (247, 247, 231);
public static final RGB conflictFgYellow = new RGB (91, 71, 17);
public static final RGB stateBlue = new RGB(196, 232, 230);
public static final RGB stateRed = new RGB(232, 196, 196);
public static final RGB stateYellow = new RGB(218, 214, 155);
public static final RGB stateBrightYellow = new RGB(241, 237, 122);
public static final RGB conflictBgBlue = new RGB(231, 244, 247);
public static final RGB conflictFgBlue = new RGB(17, 62, 91);
public static final RGB conflictBgYellow = new RGB(247, 247, 231);
public static final RGB conflictFgYellow = new RGB(91, 71, 17);
}
......@@ -12,21 +12,23 @@ import org.eclipse.core.runtime.CoreException;
import de.in.tum.www2.cup.ast.Name;
import de.in.tum.www2.cup.ast.ParserResult;
import de.tum.in.www2.cupplugin.model.Model;
public class GeneratedFileLocator {
private WeakHashMap<String,IFile> currentTargetFile;
public GeneratedFileLocator () {
currentTargetFile = new WeakHashMap<String,IFile>();
private WeakHashMap<String, IFile> currentTargetFile;
public GeneratedFileLocator() {
currentTargetFile = new WeakHashMap<String, IFile>();
}
private void scanResources(List<IFile> candidates,
String filename, IContainer parent) throws CoreException {
private void scanResources(List<IFile> candidates, String filename,
IContainer parent) throws CoreException {
for (IResource res : parent.members()) {
if (res instanceof IContainer) {
// System.out.println("found container: " + res.getName());
/*
* System.out.println("found container: " + res.getName());
* TODO: REMOVE OUTPUT
*/
scanResources(candidates, filename, (IContainer) res);
} else if (res instanceof IFile) {
IFile file = (IFile) res;
......@@ -43,7 +45,10 @@ public class GeneratedFileLocator {
return null;
List<IFile> candidates = new ArrayList<IFile>();
IProject project = cupFile.getProject();
System.err.println("Searching in project : " + project.getName());
/*
* System.err.println("Searching in project : " + project.getName());
* TODO: REMOVE OUTPUT
*/
String className = "Parser";
if (result.className != null) {
Name name = result.className.getName();
......@@ -60,21 +65,24 @@ public class GeneratedFileLocator {
return candidates;
}
public IFile find(IFile cupFile, ParserResult result) {
String fullPath = cupFile.getFullPath().toString();
if (currentTargetFile.containsKey(fullPath)) {
IFile currentFile = currentTargetFile.get(fullPath);
// System.out.println("cached!");
/* System.out.println("cached!"); TODO: REMOVE OUTPUT */
return currentFile;
} else {
currentTargetFile.remove(fullPath);
}
List<IFile> candidates = findCandidates(cupFile, result);
// if (candidates.size() == 0)
// System.out.println("Found no candidates!");
// if (candidates.size() == 0)
/* System.out.println("Found no candidates!"); TODO: REMOVE OUTPUT */
for (IFile candidate : candidates) {
// System.out.println("candidate: " + candidate.getName());
/*
* System.out.println("candidate: " + candidate.getName()); TODO:
* REMOVE OUTPUT
*/
if (currentTargetFile.containsKey(fullPath)) {
// TODO: check if better than candidate.
// e.g. if package matches
......
package de.tum.in.www2.cupplugin;
public class Pair<A,B> {
public class Pair<A, B> {
private A first;
private B second;
......@@ -8,15 +8,14 @@ public class Pair<A,B> {
public A getFirst() {
return first;
}
public B getSecond() {
return second;
}
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
}
......@@ -2,13 +2,11 @@ package de.tum.in.www2.cupplugin;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
......@@ -20,77 +18,78 @@ import de.tum.in.www2.cupplugin.editors.CupTextEditor;
import de.tum.in.www2.cupplugin.editors.MultiPageEditor;
public final class PluginUtility {
public static void showMessage(String message) {
MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getDisplay().getActiveShell());
MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getDisplay()
.getActiveShell());
mb.setMessage(message);
mb.open();
}
public static void fullSyntaxRefresh (ISourceViewer sourceViewer) {
System.out.println(">>>>>>>>>>>>>> FULL REFRESH OF SYNTAX HIGHLIGHTING! <<<<<<<<<<<<<<<");
sourceViewer.invalidateTextPresentation();
}
public static void fullSyntaxRefresh(ISourceViewer sourceViewer) {
/*
* System.out.println(
* ">>>>>>>>>>>>>> FULL REFRESH OF SYNTAX HIGHLIGHTING! <<<<<<<<<<<<<<<"
* ); TODO: REMOVE OUTPUT
*/
sourceViewer.invalidateTextPresentation();
}
public static Range getRangeFromSelection (CupTextEditor editor)
{
ITextSelection textSelection = (ITextSelection) editor
.getSite().getSelectionProvider().getSelection();
public static Range getRangeFromSelection(CupTextEditor editor) {
ITextSelection textSelection = (ITextSelection) editor.getSite()
.getSelectionProvider().getSelection();
int offset = textSelection.getOffset();
IDocumentProvider provider = editor.getDocumentProvider();
IDocument document = provider.getDocument(editor
.getEditorInput());
IDocument document = provider.getDocument(editor.getEditorInput());
// TODO: there might be off-by-one errors here.
// Check again!
// Check again!
try {
int startLineNumber = document.getLineOfOffset(offset);
int startColumn = offset - document.getLineOffset(startLineNumber);
Position start = new Position(startLineNumber+1, startColumn+1, offset+1);
Position start = new Position(startLineNumber + 1, startColumn + 1,
offset + 1);
int endOffset = offset + textSelection.getLength();
int endLineNumber = document.getLineOfOffset(endOffset);
int endColumn = endOffset - document.getLineOffset(endLineNumber);
Position end = new Position(endLineNumber+1, endColumn+1, endOffset);
Position end = new Position(endLineNumber + 1, endColumn + 1,
endOffset);
return new Range(start, end);
} catch (BadLocationException e) {
return null;
}
}
public static CupTextEditor getEditorFromEvent(ExecutionEvent event)
throws ExecutionException
{
MultiPageEditor mpe = (MultiPageEditor) HandlerUtil.getActiveEditorChecked(event);
public static CupTextEditor getEditorFromEvent(ExecutionEvent event)
throws ExecutionException {
MultiPageEditor mpe = (MultiPageEditor) HandlerUtil
.getActiveEditorChecked(event);
if (mpe == null)
return null;
return mpe.getEditor();
}
public static IFile getReverseFilePartner(IFile javaFile){
/* for(Tuple<IFile,IFile> e : fileMatching){
if( e.has(javaFile)){
return (IFile)e.other(javaFile);
}
}*/return null;
public static IFile getReverseFilePartner(IFile javaFile) {
/*
* for(Tuple<IFile,IFile> e : fileMatching){ if( e.has(javaFile)){
* return (IFile)e.other(javaFile); } }
*/return null;
}
public static void addFileTuple(IFile cupFile, IFile javaFile){
if(cupFile == null || javaFile == null)
public static void addFileTuple(IFile cupFile, IFile javaFile) {
if (cupFile == null || javaFile == null)
return;
/*
for(Tuple<IFile,IFile> e : fileMatching){
if(e.has(cupFile) || e.has(javaFile)){
fileMatching.remove(e);
}
}*/
// fileMatching.add(new Tuple<IFile, IFile>(cupFile, javaFile));
* for(Tuple<IFile,IFile> e : fileMatching){ if(e.has(cupFile) ||
* e.has(javaFile)){ fileMatching.remove(e); } }
*/
// fileMatching.add(new Tuple<IFile, IFile>(cupFile, javaFile));
return;
}
......
......@@ -3,30 +3,24 @@ package de.tum.in.www2.cupplugin.commands;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.handlers.HandlerUtil;
import de.in.tum.www2.cup.Position;
import de.in.tum.www2.cup.Range;
import de.in.tum.www2.cup.analysis.FindAtPositionVisitor;
import de.in.tum.www2.cup.ast.AbstractNode;
import de.in.tum.www2.cup.ast.IHasDeclarationReference;
import de.in.tum.www2.cup.ast.IWithName;
import de.in.tum.www2.cup.ast.ParserResult;
import de.tum.in.www2.cupplugin.PluginUtility;
import de.tum.in.www2.cupplugin.editors.CupTextEditor;
import de.tum.in.www2.cupplugin.editors.MultiPageEditor;
import de.tum.in.www2.cupplugin.model.Model;
public class OpenDeclarationHandler extends AbstractHandler {
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
// TODO: this has been copied from the DefinitionHandler, except
// that IHasDefinitionReference -> IHasDeclarationReference
// an getDefinition() -> getDeclaration()
// that IHasDefinitionReference -> IHasDeclarationReference
// an getDefinition() -> getDeclaration()
CupTextEditor editor = PluginUtility.getEditorFromEvent(event);
Range range = PluginUtility.getRangeFromSelection(editor);
if (range == null)
......@@ -39,14 +33,18 @@ public class OpenDeclarationHandler extends AbstractHandler {
AbstractNode node = result.findAtPosition(range.getBegin());
if (node != null) {
AbstractNode parent = node.getParent();
if (parent != null && parent instanceof IHasDeclarationReference) {
AbstractNode target = ((IHasDeclarationReference) parent).getDeclaration();
if (parent != null
&& parent instanceof IHasDeclarationReference) {
AbstractNode target = ((IHasDeclarationReference) parent)
.getDeclaration();
if (target != null && target instanceof IWithName) {
Position targetPosition = target.getBegin();
String name = ((IWithName) target).getNameStringOrNull();
String name = ((IWithName) target)
.getNameStringOrNull();
if (name != null) {
int length = name.length();
editor.selectAndReveal(targetPosition.getOffsetFromStart(), length);
editor.selectAndReveal(
targetPosition.getOffsetFromStart(), length);
}
}
}
......
......@@ -3,26 +3,15 @@ package de.tum.in.www2.cupplugin.commands;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.texteditor.IDocumentProvider;
import de.in.tum.www2.cup.Position;
import de.in.tum.www2.cup.Range;
import de.in.tum.www2.cup.analysis.FindAtPositionVisitor;
import de.in.tum.www2.cup.ast.AbstractNode;
import de.in.tum.www2.cup.ast.IHasDefinitionReference;
import de.in.tum.www2.cup.ast.IWithName;
import de.in.tum.www2.cup.ast.Name;
import de.in.tum.www2.cup.ast.NonTerminalDeclaration;
import de.in.tum.www2.cup.ast.ParserResult;
import de.in.tum.www2.cup.ast.Production;
import de.in.tum.www2.cup.ast.ProductionSymbolRef;
import de.tum.in.www2.cupplugin.PluginUtility;
import de.tum.in.www2.cupplugin.editors.CupTextEditor;
import de.tum.in.www2.cupplugin.editors.MultiPageEditor;
import de.tum.in.www2.cupplugin.model.Model;
public class OpenDefinitionHandler extends AbstractHandler {
......@@ -35,7 +24,8 @@ public class OpenDefinitionHandler extends AbstractHandler {
if (range == null)
return null;
IDocument doc = editor.getDocumentProvider().getDocument(editor.getEditorInput());
IDocument doc = editor.getDocumentProvider().getDocument(
editor.getEditorInput());
Model model = Model.getInstanceForDocument(doc);
ParserResult result = model.getAstModel();
......@@ -45,13 +35,16 @@ public class OpenDefinitionHandler extends AbstractHandler {
if (node != null) {
AbstractNode parent = node.getParent();
if (parent != null && parent instanceof IHasDefinitionReference) {
AbstractNode target = ((IHasDefinitionReference) parent).getDefinition();
AbstractNode target = ((IHasDefinitionReference) parent)
.getDefinition();
if (target != null && target instanceof IWithName) {
Position targetPosition = target.getBegin();
String name = ((IWithName) target).getNameStringOrNull();
String name = ((IWithName) target)
.getNameStringOrNull();
if (name != null) {
int length = name.length();
editor.selectAndReveal(targetPosition.getOffsetFromStart(), length);
editor.selectAndReveal(
targetPosition.getOffsetFromStart(), length);
}
}
}
......
......@@ -17,10 +17,7 @@ import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.HandlerUtil;
import de.in.tum.www2.cup.Range;
import de.in.tum.www2.cup.analysis.FindAtPositionVisitor;
import de.in.tum.www2.cup.analysis.Visitor;
import de.in.tum.www2.cup.ast.AbstractNode;
import de.in.tum.www2.cup.ast.IWithName;
......@@ -28,24 +25,21 @@ import de.in.tum.www2.cup.ast.Name;
import de.in.tum.www2.cup.ast.NonTerminal;
import de.in.tum.www2.cup.ast.ParserResult;
import de.in.tum.www2.cup.ast.Production;
import de.in.tum.www2.cup.ast.ProductionRight;
import de.in.tum.www2.cup.ast.ProductionSymbolRef;
import de.in.tum.www2.cup.ast.Terminal;
import de.tum.in.www2.cupplugin.PluginUtility;
import de.tum.in.www2.cupplugin.editors.CupTextEditor;
import de.tum.in.www2.cupplugin.editors.MultiPageEditor;
import de.tum.in.www2.cupplugin.editors.RevisionManager;
import de.tum.in.www2.cupplugin.model.Model;
public class RenameSymbolHandler extends AbstractHandler {
static class RenamingVisitor extends Visitor<Object>
{
static class RenamingVisitor extends Visitor<Object> {
private IDocument doc;
private String oldName;
private String newName;
private int oldNameLength;
public RenamingVisitor(IDocument doc, String oldName, String newName) {
this.doc = doc;
this.oldName = oldName;
......@@ -58,8 +52,8 @@ public class RenameSymbolHandler extends AbstractHandler {
return;
if (withName.getNameStringOrNull().equals(oldName)) {
// We do not need to track how the offset shifts,
// because the Location patcher will take care of that
// We do not need to track how the offset shifts,
// because the Location patcher will take care of that
// anyway when we edit the document.
AbstractNode node = (AbstractNode) withName;
......@@ -77,7 +71,7 @@ public class RenameSymbolHandler extends AbstractHandler {
public void postVisit(ProductionSymbolRef ref, Object obj) {
patch(ref);
}
@Override
public void postVisit(NonTerminal nt, Object obj) {
patch(nt);
......@@ -87,23 +81,23 @@ public class RenameSymbolHandler extends AbstractHandler {
public void postVisit(Production p, Object obj) {
patch(p);
}
@Override
public void postVisit(Terminal t, Object obj) {
patch(t);
}
}
static class RenameDialog extends TitleAreaDialog {
private Text newNameControl;
private String oldName;
private String newName;
public String getNewName() {
return newName;
}
public RenameDialog(String oldName, Shell parentShell) {
super(parentShell);
this.oldName = oldName;
......@@ -117,12 +111,12 @@ public class RenameSymbolHandler extends AbstractHandler {
IMessageProvider.INFORMATION);
}
@Override
protected void okPressed() {
newName = newNameControl.getText();
super.okPressed();
}
@Override
protected void okPressed() {
newName = newNameControl.getText();
super.okPressed();
}
@Override
protected Control createDialogArea(Composite parent) {
Composite area = (Composite) super.createDialogArea(parent);
......@@ -139,7 +133,7 @@ public class RenameSymbolHandler extends AbstractHandler {
GridData dataOldName = new GridData();
dataOldName.grabExcessHorizontalSpace = true;
dataOldName.horizontalAlignment = GridData.FILL;
Label dummy = new Label(container, SWT.BORDER);
dummy.setText(oldName);
dummy.setLayoutData(dataOldName);
......@@ -154,7 +148,7 @@ public class RenameSymbolHandler extends AbstractHandler {
newNameControl = new Text(container, SWT.BORDER);
newNameControl.setLayoutData(dataNewName);
newNameControl.setText(oldName);
return area;
}
......@@ -190,14 +184,18 @@ public class RenameSymbolHandler extends AbstractHandler {
IDocument doc = editor.getDocument();
Model model = Model.getInstanceForDocument(doc);
ParserResult ast = model.getAstModel();
if (ast == null || RevisionManager.get(doc) != model.getAstModelRevisionNumber()) {
PluginUtility.showMessage("Can't rename symbols, because a valid, " +
"updated AST is not available.");
if (ast == null
|| RevisionManager.get(doc) != model
.getAstModelRevisionNumber()) {
PluginUtility
.showMessage("Can't rename symbols, because a valid, "
+ "updated AST is not available.");
return null;
}
editor.beginCompoundChange();
RenamingVisitor visitor = new RenamingVisitor(doc, oldName, renameDialog.getNewName());
RenamingVisitor visitor = new RenamingVisitor(doc, oldName,
renameDialog.getNewName());
ast.accept(visitor, null);
editor.endCompoundChange();
......
......@@ -28,7 +28,6 @@ public class Controller {
// TODO: we need to remove the editor from the list, when it gets disposed.
private static WeakHashMap<CupTextEditor, Controller> instances = new WeakHashMap<CupTextEditor, Controller>();
// The Job that's executed after a document change is detected (changeJob)
private DocumentDidChangeJob myJob = null;
......@@ -43,8 +42,8 @@ public class Controller {
private final EnumSet<JobsToDo> jobElements = EnumSet
.noneOf(JobsToDo.class);
private Set<IRegisterForControllerChanges> controllerObservers = Collections.newSetFromMap(
new WeakHashMap<IRegisterForControllerChanges, Boolean>());
private Set<IRegisterForControllerChanges> controllerObservers = Collections
.newSetFromMap(new WeakHashMap<IRegisterForControllerChanges, Boolean>());
public static Controller getInstance(CupTextEditor edit) {
Controller instance = instances.get(edit);
......@@ -185,7 +184,7 @@ public class Controller {
if (event.getText().equals("/")) {
possibleJobs.add(JobsToDo.parseCode);
possibleJobs.add(JobsToDo.buildTable);
}
}
break;
default:
possibleJobs.add(JobsToDo.parseCode);
......@@ -257,7 +256,7 @@ public class Controller {
default:
break;
}
}
synchronized (jobElements) {
if (jobElements.isEmpty()) {
......@@ -273,11 +272,15 @@ public class Controller {
*/
public void notifyChangeJobFinished() {
synchronized (lock) {
System.out.println("job finished -> " + changeJobReRunRequested);
/*
* System.out.println("job finished -> " + changeJobReRunRequested);
* TODO: REMOVE OUTPUT
*/
if (changeJobReRunRequested > 0) {
changeJobReRunRequested = 0;
// TODO: why is this only multiplied by 100 here, but 1000 above ???
// TODO: why is this only multiplied by 100 here, but 1000 above
// ???
long remainderOfDelay = (SET_CHANGED_AFTER_SECONDS * 100)
- (System.currentTimeMillis() - changeJobReRunRequested);
myJob.revNumber = RevisionManager.get(editor.getDocument());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment