/**
* This file Copyright (c) 2005-2008 Aptana, Inc. This program is
* dual-licensed under both the Aptana Public License and the GNU General
* Public license. You may elect to use one or the other of these licenses.
*
* This program is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. Redistribution, except as permitted by whichever of
* the GPL or APL you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or modify this
* program under the terms of the GNU General Public License,
* Version 3, as published by the Free Software Foundation. You should
* have received a copy of the GNU General Public License, Version 3 along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Aptana provides a special exception to allow redistribution of this file
* with certain other free and open source software ("FOSS") code and certain additional terms
* pursuant to Section 7 of the GPL. You may view the exception and these
* terms on the web at http://www.aptana.com/legal/gpl/.
*
* 2. For the Aptana Public License (APL), this program and the
* accompanying materials are made available under the terms of the APL
* v1.0 which accompanies this distribution, and is available at
* http://www.aptana.com/legal/apl/.
*
* You may view the GPL, Aptana's exception and additional terms, and the
* APL in the file titled license.html at the root of the corresponding
* plugin containing this source file.
*
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.ide.editor.html;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.ITypedRegion;
import com.aptana.ide.core.PluginUtils;
import com.aptana.ide.editor.css.CSSErrorManager;
import com.aptana.ide.editor.html.lexing.HTMLTokenTypes;
import com.aptana.ide.editor.html.parsing.HTMLMimeType;
import com.aptana.ide.editor.js.JSErrorManager;
import com.aptana.ide.editors.unified.FileService;
import com.aptana.ide.editors.unified.IFileLanguageService;
import com.aptana.ide.editors.unified.IFileSourceProvider;
import com.aptana.ide.editors.unified.errors.FileError;
import com.aptana.ide.editors.unified.errors.IFileError;
import com.aptana.ide.editors.unified.errors.UnifiedErrorManager;
import com.aptana.ide.editors.unified.errors.UnifiedErrorReporter;
import com.aptana.ide.lexer.Lexeme;
import com.aptana.ide.parsing.IOffsetMapper;
/**
* @author Robin Debreuil
*/
public class HTMLErrorManager extends UnifiedErrorManager
{
JSErrorManager jsErrorManager;
CSSErrorManager cssErrorManager;
/**
* HTMLErrorManager
*
* @param fileService
*/
public HTMLErrorManager(FileService fileService)
{
super(fileService, HTMLMimeType.MimeType);
jsErrorManager = new JSErrorManager(this.fileService);
cssErrorManager = new CSSErrorManager(this.fileService);
}
/**
* @see com.aptana.ide.editors.unified.errors.UnifiedErrorManager#parseForErrors(java.lang.String, java.lang.String,
* com.aptana.ide.editors.unified.IFileSourceProvider)
*/
public IFileError[] parseForErrors(String path, String source, IFileSourceProvider sourceProvider)
{
UnifiedErrorReporter reporter = new UnifiedErrorReporter(sourceProvider);
String htmlSource = extractLanguage("text/html", source, this); //$NON-NLS-1$
// Parent class has been instrcuted via constructor to handle html
IFileError[] errs = super.parseForErrors(path, htmlSource, sourceProvider);
reporter.addErrors(errs);
String jsSource = extractLanguage("text/javascript", source, jsErrorManager); //$NON-NLS-1$
IFileError[] err = jsErrorManager.parseForErrors(path, jsSource, sourceProvider);
reporter.addErrors(err);
String cssSource = extractLanguage("text/css", source, cssErrorManager); //$NON-NLS-1$
err = cssErrorManager.parseForErrors(path, cssSource, sourceProvider);
reporter.addErrors(err);
err = reporter.getErrors();
List validErrors = getValidErrors(err);
err = (IFileError[]) validErrors.toArray(new IFileError[validErrors.size()]);
// Sort the combined results by line number
Arrays.sort(err, new Comparator()
{
public int compare(Object arg0, Object arg1)
{
FileError a = (FileError) arg0;
FileError b = (FileError) arg1;
return a.getLineNumber() - b.getLineNumber();
}
});
// Sort the combined results by severity
Arrays.sort(err, new Comparator()
{
public int compare(Object arg0, Object arg1)
{
FileError a = (FileError) arg0;
FileError b = (FileError) arg1;
return b.getSeverity() - a.getSeverity();
}
});
return err;
}
/**
* This code removes invalid JS errors reported from HTML attributes. Currently only removes "invalid return" errors
* on attributes values such as onClick="return true;"
*
* @param err -
* raw error list
* @return - list of valid errors
*/
private List getValidErrors(IFileError[] err)
{
List validErrors = new ArrayList();
for (int i = 0; i < err.length; i++)
{
IFileError fe = err[i];
if (!"invalid return".equalsIgnoreCase(fe.getMessage())) //$NON-NLS-1$
{
validErrors.add(fe);
}
else
{
try
{
ITypedRegion region = fileService.getPartitionAtOffset(fe.getOffset());
IFileLanguageService service = fileService.getLanguageService(HTMLMimeType.MimeType);
if (service != null && service.getOffsetMapper() != null)
{
IOffsetMapper mapper = service.getOffsetMapper();
int index = mapper.getLexemeIndexFromDocumentOffset(region.getOffset());
if (index - 1 > 0 && index - 1 < mapper.getLexemeList().size())
{
Lexeme prev = mapper.getLexemeAtIndex(index - 1);
// Only remove when previous lexeme is = and is HTML since that will signify the start of a
// attribute
if (!(prev.getLanguage().equals(HTMLMimeType.MimeType) && prev.getToken().getTypeIndex() == HTMLTokenTypes.EQUAL))
{
validErrors.add(fe);
}
}
}
}
catch (Exception e)
{
// Leave as valid error if attempt to remove fails for any reason
validErrors.add(fe);
}
}
}
return validErrors;
}
private String extractLanguage(String lang, String source, UnifiedErrorManager manager)
{
if (source == null || source == "") //$NON-NLS-1$
{
return ""; //$NON-NLS-1$
}
if (this.fileService != null)
{
ITypedRegion[] partitions = this.fileService.getPartitions();
ArrayList strip = new ArrayList();
if (partitions != null)
{
for (int i = 0; i < partitions.length; i++)
{
ITypedRegion partition = partitions[i];
int start = partition.getOffset();
int length = partition.getLength();
if (partition.getType().equals(lang) == false)
{
if (start >= 0 && length > 0)
{
strip.add(partition);
}
}
else
{
source = manager.processLanguagePartition(partition, source);
}
}
char[] sourceArray=source.toCharArray();
// Have to strip after the fact, as the lang partition may need to know
// its context
for (Iterator iter = strip.iterator(); iter.hasNext();)
{
ITypedRegion partition = (ITypedRegion) iter.next();
int start = partition.getOffset();
int length = partition.getLength();
stripCharsArray(sourceArray, start, length);
}
source=new String(sourceArray);
}
}
return source;
}
protected void stripCharsArray(char[] source, int start, int length)
{
char[] c = source;
int end = start + length;
if (end > c.length)
{
end = c.length;
}
for (int i = start; i < end; i++)
{
if (c[i] != '\r' && c[i] != '\n')
{
c[i] = ' ';
}
}
}
/**
* filter messages before they go out, return 'null' to have the message omitted
*
* @param message
* @return String
*/
public String filterMessage(String message)
{
message = message.replaceFirst("discarding", "should discard"); //$NON-NLS-1$ //$NON-NLS-2$
message = message.replaceFirst("inserting", "should insert"); //$NON-NLS-1$ //$NON-NLS-2$
message = message.replaceFirst("trimming", "should trim"); //$NON-NLS-1$ //$NON-NLS-2$
return super.filterMessage(message);
}
/**
* Returns the preference store
*
* @return IPreferenceStore
*/
protected IPreferenceStore getPreferenceStore()
{
if (PluginUtils.isPluginLoaded(HTMLPlugin.getDefault()))
{
return HTMLPlugin.getDefault().getPreferenceStore();
}
else
{
return null;
}
}
}