/*******************************************************************************
* Copyright (c) 2010 György Orosz.
* 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:
* György Orosz - initial API and implementation
******************************************************************************/
package org.erlide.wrangler.refactoring.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
import org.erlide.engine.ErlangEngine;
import org.erlide.engine.model.IErlElement;
import org.erlide.engine.model.erlang.IErlFunctionClause;
import org.erlide.engine.model.root.IErlModel;
import org.erlide.engine.model.root.IErlModule;
import org.erlide.ui.editors.erl.ErlangEditor;
import org.erlide.util.ErlLogger;
import org.erlide.wrangler.refactoring.backend.ChangedFile;
import org.erlide.wrangler.refactoring.selection.IErlMemberSelection;
import com.ericsson.otp.erlang.OtpErlangLong;
import com.ericsson.otp.erlang.OtpErlangRangeException;
import com.ericsson.otp.erlang.OtpErlangTuple;
/**
* Utility class, which aims to collect common frequently used functions.
*
* @author Gyorgy Orosz
* @version %I%, %G%
*/
public final class WranglerUtils {
private WranglerUtils() {
}
/**
* Gets the column number from offset value
*
* @param offset
* offset in the current editor
* @param line
* line number in the current editor
* @param doc
* current document, in which the offset is calculated
* @return column number
*/
static public int calculateColumnFromOffset(final int offset, final int line,
final IDocument doc) {
int lineOffset;
try {
lineOffset = doc.getLineOffset(line);
final int ret = offset - lineOffset < 0 ? 0 : offset - lineOffset;
return ret + 1;
} catch (final BadLocationException e) {
ErlLogger.error(e);
return 0;
}
}
/**
* Calculates offset from the given line and column number.
*
* @param line
* line number
* @param column
* column number
* @param doc
* the document in which the position is given
* @return offset in the document
* @throws BadLocationException
* if the given position doesn't exist
*/
static public int calculateOffsetFromPosition(final int line, final int column,
final IDocument doc) throws BadLocationException {
return doc.getLineOffset(line - 1) + column - 1;
}
/**
* Calculate offset from a position.
*
* @param pos
* Position wrapped in an Erlang Tuple: {Line, Column}
* @param doc
* the current document
* @return offset in the document
* @throws OtpErlangRangeException
* if the tuple is not well formed
* @throws BadLocationException
* if the given position doesn't exist
*/
static public int calculateOffsetFromErlangPos(final OtpErlangTuple pos,
final IDocument doc) throws OtpErlangRangeException, BadLocationException {
final int line = ((OtpErlangLong) pos.elementAt(0)).intValue();
final int column = ((OtpErlangLong) pos.elementAt(1)).intValue();
final int offset = calculateOffsetFromPosition(line, column, doc);
return offset - 1;
}
/**
* Gets a string from a document.
*
* @param range
* Position range
* @param doc
* source document
* @return requested string
*/
static public String getTextFromEditor(final IErlRange range, final IDocument doc) {
try {
final String s = doc.get(range.getOffset(), range.getLength());
return s;
} catch (final BadLocationException e) {
ErlLogger.error(e);
}
return "";
}
/**
* Returns a list of the given project Erlang files.
*
* @param project
* the containing project in which the scan is run
* @return module names' list
*/
static public ArrayList<String> getModuleNames(final IProject project) {
final ArrayList<IFile> erlangFiles = getModules(project);
final ArrayList<String> moduleNames = new ArrayList<>();
for (final IFile f : erlangFiles) {
moduleNames.add(removeExtension(f.getName()));
}
Collections.sort(moduleNames);
return moduleNames;
}
/**
* Get modules in an Erlang project.
*
* @param project
* the project in which the scan is run
* @return module list
*/
static public ArrayList<IFile> getModules(final IProject project) {
final ArrayList<IFile> erlangFiles = new ArrayList<>();
try {
findModulesRecursively(project, erlangFiles);
} catch (final CoreException e) {
ErlLogger.error(e);
return new ArrayList<>();
}
return erlangFiles;
}
static private void findModulesRecursively(final IResource res,
final List<IFile> files) throws CoreException {
if (res instanceof IContainer) {
final IContainer c = (IContainer) res;
for (final IResource r : c.members()) {
findModulesRecursively(r, files);
}
} else if (res instanceof IFile) {
final IFile file = (IFile) res;
if (isErlangFile(file)) {
files.add(file);
}
}
}
static public boolean isErlangFile(final IFile file) {
return "erl".equals(file.getFileExtension())
|| "hrl".equals(file.getFileExtension());
}
/**
* Removes the extension from a filename
*
* @param fileName
* the file's name
* @return file name without the extension
*/
static public String removeExtension(final String fileName) {
return fileName.substring(0, fileName.lastIndexOf("."));
}
/**
* Returns the selection's text from the active editor's text file.
*
* @param start
* tuple with 2 element: line:int(), col:int()
* @param end
* tuple with 2 element: line:int(), col:int()
* @param doc
* the document in which the scan is run
* @return selected text
* @throws OtpErlangRangeException
* if the position tuple is not well formed
* @throws BadLocationException
* if the given position does not exist
*/
static public String getTextSegment(final OtpErlangTuple start,
final OtpErlangTuple end, final IDocument doc)
throws OtpErlangRangeException, BadLocationException {
final int startOffset = calculateOffsetFromErlangPos(start, doc);
final int endOffset = calculateOffsetFromErlangPos(end, doc);
return getTextSegment(startOffset, endOffset, doc);
}
/**
* Returns the selection's text from the active editor's text file.
*
* @param startOffset
* start offset
* @param endOffset
* end offset
* @param doc
* selected document
* @return selected text
* @throws BadLocationException
* if the positions does not name a corrsssect place
*/
public static String getTextSegment(final int startOffset, final int endOffset,
final IDocument doc) throws BadLocationException {
final String s = doc.get(startOffset, endOffset - startOffset);
return s;
}
/*
* static public String getTextSegment(int startLine, int startPos, int
* endLine, int endPos, IFile file) throws BadLocationException { IDocument
* doc = getDocument(file); return getTextSegment(startLine, startPos,
* endLine, endPos, doc); }
*/
protected static String getTextSegment(final int startLine, final int startPos,
final int endLine, final int endPos, final IDocument doc)
throws BadLocationException {
final int startOffset = calculateOffsetFromPosition(startLine, startPos, doc);
final int endOffset = calculateOffsetFromPosition(endLine, endPos, doc);
return getTextSegment(startOffset, endOffset, doc);
}
/**
* Highlight the given selection in the given editor
*
* @param startOffset
* selection's starting offset
* @param endOffset
* selection's ending offset
* @param editor
* editor in which the highlightion should be done
*/
static public void highlightOffsetSelection(final int startOffset,
final int endOffset, final ITextEditor editor) {
highlightSelection(startOffset, endOffset - startOffset, editor);
}
/**
* Highlight the given selection in the given editor
*
* @param offset
* selection's starting offset
* @param length
* selection's length
* @param editor
* editor in which the highlightion should be done
*/
static protected void highlightSelection(final int offset, final int length,
final ITextEditor editor) {
editor.setHighlightRange(offset, length, true);
editor.selectAndReveal(offset, length);
}
/**
* Highlight the given selection in the editor which contains the given
* IErlMemberSelection
*
* @param offset
* selection's starting offset
* @param length
* selection's length
* @param selection
* Selected Erlang member
*/
public static void highlightSelection(final int offset, final int length,
final IErlMemberSelection selection) {
final ITextEditor editor = (ITextEditor) openFile(selection.getFile());
highlightSelection(offset, length, editor);
}
/**
* Highlights the given function clause
*
* @param clause
* erlang function clause
*/
public static void highlightSelection(final IErlFunctionClause clause) {
int offset, length;
offset = clause.getNameRange().getOffset();
length = clause.getNameRange().getLength();
final IErlModule module = ErlangEngine.getInstance().getModelUtilService()
.getModule(clause);
final IEditorPart editor = openFile((IFile) module.getResource());
highlightSelection(offset, length, (ITextEditor) editor);
}
/**
* Opens the given file with the Eclipse default editor
*
* @param file
* file
* @return the opened editor
*/
static public IEditorPart openFile(final IFile file) {
final IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
.getActivePage();
/*
* IEditorDescriptor desc =
* PlatformUI.getWorkbench().getEditorRegistry()
* .getDefaultEditor(file.getName());
*/
try {
return IDE.openEditor(page, file);
// return page.openEditor(new FileEditorInput(file), desc.getId());
} catch (final PartInitException e) {
ErlLogger.error(e);
}
return null;
}
/**
* Returns the actual editor's document
*
* @return document in the current editor
*/
static public IDocument getDocument() {
final IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
.getActivePage();
final IEditorPart part = page.getActiveEditor();
final ITextEditor editor = (ITextEditor) part;
final IDocumentProvider dp = editor.getDocumentProvider();
final IDocument doc = dp.getDocument(editor.getEditorInput());
return doc;
}
/**
* Gets the document which belongs to a file
*
* @param file
* corresponding file
* @return document
*/
static public IDocument getDocument(final IFile file) {
return new Document(getFileContent(file));
}
/**
* Returns the corresponding IDocument object
*
* @param editor
* editor which has the document
* @return document is conatained by the editor
*/
static public IDocument getDocument(final ITextEditor editor) {
final IFileEditorInput input = (IFileEditorInput) editor.getEditorInput();
final IDocument doc = editor.getDocumentProvider().getDocument(input);
return doc;
}
static private String getFileContent(final IFile file) {
try (final InputStream in = file.getContents();
final ByteArrayOutputStream out = new ByteArrayOutputStream()) {
final byte[] buf = new byte[1024];
int read = in.read(buf);
while (read > 0) {
out.write(buf, 0, read);
read = in.read(buf);
}
return out.toString();
} catch (final CoreException e) {
ErlLogger.error(e);
} catch (final IOException e) {
ErlLogger.error(e);
}
return "";
}
/**
* Gets IFile object from path string
*
* @param pathString
* file path
* @return IFile object
*/
static public IFile getFileFromPath(final String pathString) {
final Path path = new Path(pathString);
return getFileFromPath(path);
}
/**
* Gets an IFile object from Path
*
* @param path
* file path
* @return IFile object
*/
public static IFile getFileFromPath(final IPath path) {
final IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
final IFile[] files = // root.findFilesForLocation(path);
root.findFilesForLocationURI(
org.eclipse.core.filesystem.URIUtil.toURI(path));
if (files.length > 0) {
return files[0];// else
}
return root.getFile(path);
/*
* if (file != null) return file;
*/
/*
* else throw new WranglerException("File not found!");
*/
}
/**
* Notifies Erlide about the changed files.
*
* @param changedFiles
* changed files
*/
public static void notifyErlide(final List<ChangedFile> changedFiles) {
final IErlModel model = ErlangEngine.getInstance().getModel();
for (final ChangedFile f : changedFiles) {
IFile file;
try {
file = getFileFromPath(f.getNewPath());
final IErlElement element = model.findElement(file);
final IErlModule m = (IErlModule) element;
m.resourceChanged(null);
final IEditorPart editor = GlobalParameters.getEditor();
if (editor instanceof ErlangEditor) {
((ErlangEditor) editor).resetAndCacheScannerAndParser();
}
model.notifyChange(m);
} catch (final Exception e) {
ErlLogger.error(e);
}
}
}
}