From a878a642073b4987e77ccf6a251d72dd5051f75a Mon Sep 17 00:00:00 2001 From: fabioz Date: Thu, 10 May 2012 15:29:47 -0300 Subject: [PATCH] APSTUD-4778: fixed halt condition and also made improvement where ongoing parses will be seen and used instead of requesting new parses. --- .gitignore | 3 + .../com/aptana/core/util/ImmutableTupleN.java | 108 ++++++ .../src/com/aptana/core/util/StringUtil.java | 28 ++ .../editor/js/parsing/JSParseState.java | 34 +- .../aptana/index/core/build/BuildContext.java | 68 ++-- .../src/com/aptana/parsing/IParseState.java | 13 +- .../aptana/parsing/IParseStateCacheKey.java | 33 ++ .../src/com/aptana/parsing/Messages.java | 1 + .../src/com/aptana/parsing/ParseState.java | 61 ++-- .../aptana/parsing/ParseStateCacheKey.java | 27 ++ .../ParseStateCacheKeyWithComments.java | 70 ++++ .../com/aptana/parsing/ParserPoolFactory.java | 86 +---- .../src/com/aptana/parsing/ParsingEngine.java | 296 +++++++++++++++++ .../com/aptana/parsing/messages.properties | 1 + .../com/aptana/core/util/AllUtilTests.java | 1 + .../aptana/core/util/ImmutableTupleNTest.java | 47 +++ .../.settings/org.eclipse.jdt.core.prefs | 282 +++++++++++++++- .../.settings/org.eclipse.jdt.ui.prefs | 4 + .../index/core/build/BuildContextTest.java | 73 +++++ .../index/core/tests/AllIndexCoreTests.java | 2 + .../ParseStateCacheKeyWithCommentsTest.java | 32 ++ .../src/com/aptana/parsing/pool/AllTests.java | 26 ++ .../parsing/pool/ParsingPoolFactoryTest.java | 310 ++++++++++++++++++ .../com/aptana/parsing/tests/AllTests.java | 5 + .../aptana/parsing/tests/ParseStateTest.java | 51 +++ 25 files changed, 1497 insertions(+), 165 deletions(-) create mode 100644 plugins/com.aptana.core/src/com/aptana/core/util/ImmutableTupleN.java create mode 100644 plugins/com.aptana.parsing/src/com/aptana/parsing/IParseStateCacheKey.java create mode 100644 plugins/com.aptana.parsing/src/com/aptana/parsing/ParseStateCacheKey.java create mode 100644 plugins/com.aptana.parsing/src/com/aptana/parsing/ParseStateCacheKeyWithComments.java create mode 100644 plugins/com.aptana.parsing/src/com/aptana/parsing/ParsingEngine.java create mode 100644 tests/com.aptana.core.tests/src/com/aptana/core/util/ImmutableTupleNTest.java create mode 100644 tests/com.aptana.index.core.tests/.settings/org.eclipse.jdt.ui.prefs create mode 100644 tests/com.aptana.index.core.tests/src/com/aptana/index/core/build/BuildContextTest.java create mode 100644 tests/com.aptana.parsing.tests/src/com/aptana/parsing/ParseStateCacheKeyWithCommentsTest.java create mode 100644 tests/com.aptana.parsing.tests/src/com/aptana/parsing/pool/AllTests.java create mode 100644 tests/com.aptana.parsing.tests/src/com/aptana/parsing/pool/ParsingPoolFactoryTest.java create mode 100644 tests/com.aptana.parsing.tests/src/com/aptana/parsing/tests/ParseStateTest.java diff --git a/.gitignore b/.gitignore index a5d9d136ee..aabdd95664 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ tests/com.aptana.scripting.tests/application-bundles/**/cache*.yml tests/com.aptana.scripting.tests/user-bundles/**/cache*.yml tests/com.aptana.scripting.tests/project-bundles/**/cache*.yml tests/com.aptana.studio.tests.all/wintest +.metadata +.recordings +runtime-New_configuration/ diff --git a/plugins/com.aptana.core/src/com/aptana/core/util/ImmutableTupleN.java b/plugins/com.aptana.core/src/com/aptana/core/util/ImmutableTupleN.java new file mode 100644 index 0000000000..7341fef479 --- /dev/null +++ b/plugins/com.aptana.core/src/com/aptana/core/util/ImmutableTupleN.java @@ -0,0 +1,108 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ +package com.aptana.core.util; + +/** + * Defines a tuple of objects, adding equals and hashCode operations. It's expected that any object added to this tuple + * is immutable (properly implementing hashCode() and equals()). + * + * @author Fabio + */ +public class ImmutableTupleN +{ + + private final Object[] tuple; + private int hash; + + public ImmutableTupleN(Object... tuple) + { + this.tuple = tuple; + } + + @Override + public boolean equals(Object obj) + { + if (!(obj instanceof ImmutableTupleN)) + { + return false; + } + + ImmutableTupleN t2 = (ImmutableTupleN) obj; + if (t2.tuple.length != this.tuple.length) + { + return false; + } + for (int i = 0; i < tuple.length; i++) + { + Object o1 = tuple[i]; + Object o2 = t2.tuple[i]; + if (o1 != o2) + { + if (o1 == null || o2 == null) + { + return false; + } + if (!o1.equals(o2)) + { + return false; + } + } + } + + return true; + } + + @Override + public int hashCode() + { + if (hash != 0) + { + return hash; + } + int ret = 1; + int len = tuple.length; + for (int i = 0; i < len; i++) + { + Object o = tuple[i]; + if (o == null) + { + ret += (i * 3); + } + else + { + int objHash = o.hashCode(); + ret += (i * 7 + (3 * objHash)); + } + } + hash = 7 + ret; + return hash; + } + + @Override + public String toString() + { + return this.getClass().getName() + "[" + StringUtil.join(", ", tuple) + "]"; + } + + /** + * @return the size of this tuple. + */ + public int size() + { + return this.tuple.length; + } + + /** + * @param i + * the position of the element we want to get at the tuple. + */ + public Object getAt(int i) + { + return this.tuple[i]; + } +} diff --git a/plugins/com.aptana.core/src/com/aptana/core/util/StringUtil.java b/plugins/com.aptana.core/src/com/aptana/core/util/StringUtil.java index 143a6f3993..966427ba44 100644 --- a/plugins/com.aptana.core/src/com/aptana/core/util/StringUtil.java +++ b/plugins/com.aptana.core/src/com/aptana/core/util/StringUtil.java @@ -344,6 +344,34 @@ public static String join(String delimiter, Collection items) return (items != null) ? join(delimiter, items.toArray(new String[items.size()])) : null; } + /** + * Create a string by concatenating the elements of a collection using a delimiter between each item + * + * @param delimiter + * The text to place between each element in the array + * @param items + * The array of items + * @return The resulting string + */ + public static String join(String delimiter, Object... items) + { + String[] s = new String[items.length]; + for (int i = 0; i < items.length; i++) + { + Object item = items[i]; + if (item == null) + { + s[i] = "null"; //$NON-NLS-1$ + + } + else + { + s[i] = item.toString(); + } + } + return join(delimiter, s); + } + /** * Create a string by concatenating the elements of a string array using a delimiter between each item * diff --git a/plugins/com.aptana.editor.js/src/com/aptana/editor/js/parsing/JSParseState.java b/plugins/com.aptana.editor.js/src/com/aptana/editor/js/parsing/JSParseState.java index f235829e4f..6170e10f2b 100644 --- a/plugins/com.aptana.editor.js/src/com/aptana/editor/js/parsing/JSParseState.java +++ b/plugins/com.aptana.editor.js/src/com/aptana/editor/js/parsing/JSParseState.java @@ -11,8 +11,9 @@ import com.aptana.core.logging.IdeLog; import com.aptana.editor.js.JSPlugin; -import com.aptana.parsing.IParseState; +import com.aptana.parsing.IParseStateCacheKey; import com.aptana.parsing.ParseState; +import com.aptana.parsing.ParseStateCacheKeyWithComments; /** * JSParseState @@ -122,32 +123,11 @@ public void setCollectComments(boolean flag) commentContentStack.peek().attachComments = flag; } - public boolean requiresReparse(IParseState newState) + @Override + public IParseStateCacheKey getCacheKey(String contentTypeId) { - // We can't compare, assume re-parse - if (!(newState instanceof JSParseState)) - { - return true; - } - - if (super.requiresReparse(newState)) - { - return true; - } - - JSParseState newParseState = (JSParseState) newState; - if (newParseState.attachComments() && !attachComments()) - { - // we need comments attached, and old one doesn't have them attached. re-parse - return true; - } - - if (newParseState.collectComments() && !collectComments()) - { - // we need comments collected, and old one doesn't have them collected. re-parse - return true; - } - - return false; + IParseStateCacheKey cacheKey = super.getCacheKey(contentTypeId); + return new ParseStateCacheKeyWithComments(attachComments(), collectComments(), cacheKey); } + } diff --git a/plugins/com.aptana.index.core/src/com/aptana/index/core/build/BuildContext.java b/plugins/com.aptana.index.core/src/com/aptana/index/core/build/BuildContext.java index e356057031..28e7482c47 100644 --- a/plugins/com.aptana.index.core/src/com/aptana/index/core/build/BuildContext.java +++ b/plugins/com.aptana.index.core/src/com/aptana/index/core/build/BuildContext.java @@ -1,3 +1,10 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ package com.aptana.index.core.build; import java.io.ByteArrayInputStream; @@ -28,6 +35,7 @@ import com.aptana.core.util.StringUtil; import com.aptana.index.core.IndexPlugin; import com.aptana.parsing.IParseState; +import com.aptana.parsing.IParseStateCacheKey; import com.aptana.parsing.ParseState; import com.aptana.parsing.ParserPoolFactory; import com.aptana.parsing.ast.IParseError; @@ -41,6 +49,7 @@ public class BuildContext private IFile file; protected Map> problems; private IParseState fParseState; + private IParseStateCacheKey fParseStateCacheKey; private String fContents; @@ -90,43 +99,32 @@ public synchronized IParseRootNode getAST(IParseState parseState) throws CoreExc parseState.setEditState(getContents()); try { - boolean reparse = false; - if (fParseState == null) + String contentType = getContentType(); + IParseStateCacheKey newCacheKey = parseState.getCacheKey(contentType); + if (fParseState != null && fParseStateCacheKey != null + && !fParseStateCacheKey.requiresReparse(newCacheKey)) { - reparse = true; + // copy over errors from old parse state to new one since we're not re-parsing + parseState.copyErrorsFrom(fParseState); + return fParseState.getParseResult(); } - else + fParseState = parseState; + fParseStateCacheKey = newCacheKey; + // FIXME What if we fail to parse? Should we catch and log that exception here and return null? + try { - reparse = fParseState.requiresReparse(parseState); - if (!reparse) - { - // copy over errors from old parse state to new one since we're not re-parsing - for (IParseError error : fParseState.getErrors()) - { - parseState.addError(error); - } - } + // FIXME The parsers need to throw a specific SyntaxException or something for us to differentiate + // between those and IO errors! + IParseRootNode ast = parse(contentType, fParseState); + fParseState.setParseResult(ast); } - - if (reparse) + catch (CoreException e) { - fParseState = parseState; - // FIXME What if we fail to parse? Should we catch and log that exception here and return null? - try - { - // FIXME The parsers need to throw a specific SyntaxException or something for us to differentiate - // between those and IO errors! - IParseRootNode ast = ParserPoolFactory.parse(getContentType(), fParseState); - fParseState.setParseResult(ast); - } - catch (CoreException e) - { - throw e; - } - catch (Exception e) - { - throw new CoreException(new Status(IStatus.ERROR, IndexPlugin.PLUGIN_ID, e.getMessage(), e)); - } + throw e; + } + catch (Exception e) + { + throw new CoreException(new Status(IStatus.ERROR, IndexPlugin.PLUGIN_ID, e.getMessage(), e)); } if (fParseState == null) { @@ -144,9 +142,15 @@ public synchronized IParseRootNode getAST(IParseState parseState) throws CoreExc } } + protected IParseRootNode parse(String contentType, IParseState parseState) throws Exception + { + return ParserPoolFactory.parse(contentType, parseState); + } + public synchronized void resetAST() { fParseState = null; + fParseStateCacheKey = null; } public synchronized String getContents() diff --git a/plugins/com.aptana.parsing/src/com/aptana/parsing/IParseState.java b/plugins/com.aptana.parsing/src/com/aptana/parsing/IParseState.java index ea099a9b98..6c33f198fa 100644 --- a/plugins/com.aptana.parsing/src/com/aptana/parsing/IParseState.java +++ b/plugins/com.aptana.parsing/src/com/aptana/parsing/IParseState.java @@ -127,12 +127,15 @@ public interface IParseState public void setProgressMonitor(IProgressMonitor monitor); /** - * Given the new parse state, does this old one encompass the requirements? (i.e. can we just re-use the parse - * result from this parse state instead of doing a re-parse?) + * @return an immutable object to be used as the key for this parse state (i.e.: object with hashCode/equals). + */ + public IParseStateCacheKey getCacheKey(String contentTypeId); + + /** + * Copies the errors from the cachedParseState to this parse state. * - * @param newState - * @return + * @param cachedParseState errors will be copied from this parse state. */ - public boolean requiresReparse(IParseState newState); + public void copyErrorsFrom(IParseState cachedParseState); } diff --git a/plugins/com.aptana.parsing/src/com/aptana/parsing/IParseStateCacheKey.java b/plugins/com.aptana.parsing/src/com/aptana/parsing/IParseStateCacheKey.java new file mode 100644 index 0000000000..65a9db1544 --- /dev/null +++ b/plugins/com.aptana.parsing/src/com/aptana/parsing/IParseStateCacheKey.java @@ -0,0 +1,33 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ +package com.aptana.parsing; + +/** + * @author Fabio + */ +public interface IParseStateCacheKey +{ + + /** + * @return if a reparse is required. It may be possible that 2 keys are equal but a reparse is still needed. (i.e.: + * done given the need of having a result with or without comments -- in which case having the comments is + * accepted if we ask for it without the comments but the other way around would need a reparse). + */ + public boolean requiresReparse(IParseStateCacheKey newCacheKey); + + /** + * Just making explicit that we must have equals/hashCode properly implemented as this will be a key in a map. + */ + public boolean equals(Object other); + + /** + * Just making explicit that we must have equals/hashCode properly implemented as this will be a key in a map. + */ + public int hashCode(); + +} diff --git a/plugins/com.aptana.parsing/src/com/aptana/parsing/Messages.java b/plugins/com.aptana.parsing/src/com/aptana/parsing/Messages.java index 1f5fa1cd32..6ecf78ceff 100644 --- a/plugins/com.aptana.parsing/src/com/aptana/parsing/Messages.java +++ b/plugins/com.aptana.parsing/src/com/aptana/parsing/Messages.java @@ -16,6 +16,7 @@ public class Messages extends NLS { private static final String BUNDLE_NAME = "com.aptana.parsing.messages"; //$NON-NLS-1$ + public static String ParserPoolFactory_Expecting_Source; public static String ParserPoolFactory_Cannot_Acquire_Parser; public static String ParserPoolFactory_Cannot_Acquire_Parser_Pool; static diff --git a/plugins/com.aptana.parsing/src/com/aptana/parsing/ParseState.java b/plugins/com.aptana.parsing/src/com/aptana/parsing/ParseState.java index a32cd52767..cf1afe79eb 100644 --- a/plugins/com.aptana.parsing/src/com/aptana/parsing/ParseState.java +++ b/plugins/com.aptana.parsing/src/com/aptana/parsing/ParseState.java @@ -15,6 +15,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; +import com.aptana.core.util.ImmutableTupleN; import com.aptana.core.util.StringUtil; import com.aptana.parsing.ast.IParseError; import com.aptana.parsing.ast.IParseRootNode; @@ -35,16 +36,17 @@ public class ParseState implements IParseState private IProgressMonitor fProgressMonitor; /** - * This holds onto the hashcode of the source for the parse. Used for determining if we need to re-parse or cache is - * valid. + * Used for determining if we need to re-parse or cache is valid. If 2 objects have the same cache-key, their parse + * results should be considered equal. */ - private int fSourceHash; + private ImmutableTupleN fCacheKey; public ParseState() { fSource = StringUtil.EMPTY; fProperties = new HashMap(); fErrors = new ArrayList(); + fCacheKey = new ImmutableTupleN(); } public void clearEditState() @@ -86,9 +88,33 @@ public void setEditState(String source) public void setEditState(String source, int startingOffset) { fSource = (source != null) ? source : StringUtil.EMPTY; - fSourceHash = fSource.hashCode(); // Store hashcode of source for use later for determining cache-busting fStartingOffset = startingOffset; fSkippedRanges = null; + + int length = fSource.length(); + if (length < 11) + { + // If it's a small string, just keep it instead of using the hashCode(). + fCacheKey = new ImmutableTupleN(length, fSource, fStartingOffset); + } + else + { + // Note: we currently use the fSource.hashCode() in order to decide if the contents of this parse equal + // the contents of another parse. Conflicts may still arise in this situation -- an md5 would be more + // accurate. As an attempt to make this just a bit better we also get 5 chars from the string from + // many locations of the string and add them to our result too, so that the chance of a collision is even + // lower. + char[] chars = new char[5]; + double factor = length / 4.0; + + chars[0] = fSource.charAt(0); // first char + chars[1] = fSource.charAt((int) factor); + chars[2] = fSource.charAt((int) (2 * factor)); + chars[3] = fSource.charAt((int) (3 * factor)); + chars[4] = fSource.charAt(length - 1); // last char + + fCacheKey = new ImmutableTupleN(length, fSource.hashCode(), new String(chars), fStartingOffset); + } } public void setParseResult(IParseRootNode result) @@ -137,6 +163,14 @@ public List getErrors() return new ArrayList(fErrors); } + public void copyErrorsFrom(IParseState cachedParseState) + { + for (IParseError error : cachedParseState.getErrors()) + { + addError(error); + } + } + public void addError(IParseError error) { fErrors.add(error); @@ -152,22 +186,9 @@ public void removeError(IParseError error) fErrors.remove(error); } - public boolean requiresReparse(IParseState newState) + public IParseStateCacheKey getCacheKey(String contentTypeId) { - // We can't compare, assume re-parse - if (!(newState instanceof ParseState)) - { - return true; - } - - ParseState newParseState = (ParseState) newState; - if (fSourceHash != newParseState.fSourceHash) - { - // source hashes don't match, requires re-parse - return true; - } - - // if starting offsets don't match, requires re-parse - return fStartingOffset != newParseState.fStartingOffset; + return new ParseStateCacheKey(contentTypeId, fCacheKey); } + } diff --git a/plugins/com.aptana.parsing/src/com/aptana/parsing/ParseStateCacheKey.java b/plugins/com.aptana.parsing/src/com/aptana/parsing/ParseStateCacheKey.java new file mode 100644 index 0000000000..f0d33c9147 --- /dev/null +++ b/plugins/com.aptana.parsing/src/com/aptana/parsing/ParseStateCacheKey.java @@ -0,0 +1,27 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ +package com.aptana.parsing; + +import com.aptana.core.util.ImmutableTupleN; + +/** + * @author Fabio + */ +public class ParseStateCacheKey extends ImmutableTupleN implements IParseStateCacheKey +{ + + public ParseStateCacheKey(Object... tuple) + { + super(tuple); + } + + public boolean requiresReparse(IParseStateCacheKey newCacheKey) + { + return !this.equals(newCacheKey); + } +} diff --git a/plugins/com.aptana.parsing/src/com/aptana/parsing/ParseStateCacheKeyWithComments.java b/plugins/com.aptana.parsing/src/com/aptana/parsing/ParseStateCacheKeyWithComments.java new file mode 100644 index 0000000000..cee72f95f3 --- /dev/null +++ b/plugins/com.aptana.parsing/src/com/aptana/parsing/ParseStateCacheKeyWithComments.java @@ -0,0 +1,70 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ +package com.aptana.parsing; + +/** + * A cache key which has options to attach and collect comments. It's done in a way where the equals/hashCode don't use + * the information on comments, but the requiresReparse does, so that if the current (cached) parse has comments it may + * be reused if the parse is asked without comments (and the other way requires a new parse). + * + * @author Fabio + */ +public class ParseStateCacheKeyWithComments implements IParseStateCacheKey +{ + + private final boolean fAttachComments; + private final boolean fCollectComments; + private final IParseStateCacheKey fParentCacheKey; + + public ParseStateCacheKeyWithComments(boolean attachComments, boolean collectComments, + IParseStateCacheKey parentCacheKey) + { + this.fParentCacheKey = parentCacheKey; + this.fAttachComments = attachComments; + this.fCollectComments = collectComments; + } + + @Override + public boolean equals(Object obj) + { + if (!(obj instanceof ParseStateCacheKeyWithComments)) + { + return false; + } + ParseStateCacheKeyWithComments otherParentCacheKey = (ParseStateCacheKeyWithComments) obj; + return fParentCacheKey.equals(otherParentCacheKey.fParentCacheKey); + } + + @Override + public int hashCode() + { + return fParentCacheKey.hashCode(); + } + + public boolean requiresReparse(IParseStateCacheKey newCacheKey) + { + if (!(newCacheKey instanceof ParseStateCacheKeyWithComments)) + { + return true; + } + ParseStateCacheKeyWithComments newCacheKeyWithComments = (ParseStateCacheKeyWithComments) newCacheKey; + if (fParentCacheKey.requiresReparse(newCacheKeyWithComments.fParentCacheKey)) + { + return true; + } + if (newCacheKeyWithComments.fAttachComments && !this.fAttachComments) + { + return true; + } + if (newCacheKeyWithComments.fCollectComments && !this.fCollectComments) + { + return true; + } + return false; + } +} diff --git a/plugins/com.aptana.parsing/src/com/aptana/parsing/ParserPoolFactory.java b/plugins/com.aptana.parsing/src/com/aptana/parsing/ParserPoolFactory.java index b6dd14a885..99749610d3 100644 --- a/plugins/com.aptana.parsing/src/com/aptana/parsing/ParserPoolFactory.java +++ b/plugins/com.aptana.parsing/src/com/aptana/parsing/ParserPoolFactory.java @@ -7,7 +7,6 @@ */ package com.aptana.parsing; -import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -18,16 +17,13 @@ import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentTypeManager; -import com.aptana.core.epl.util.LRUCache; -import com.aptana.core.logging.IdeLog; import com.aptana.core.util.CollectionsUtil; import com.aptana.core.util.EclipseUtil; import com.aptana.core.util.IConfigurationElementProcessor; import com.aptana.internal.parsing.ParserPool; -import com.aptana.parsing.ast.IParseError; import com.aptana.parsing.ast.IParseRootNode; -public class ParserPoolFactory +public class ParserPoolFactory implements ParsingEngine.IParserPoolProvider { // extension point constants private static final String PARSER_ID = "parser"; //$NON-NLS-1$ @@ -35,13 +31,9 @@ public class ParserPoolFactory private static final String ATTR_CONTENT_TYPE = "content-type"; //$NON-NLS-1$ private static ParserPoolFactory INSTANCE; - /** - * A parse cache. Keyed by combo of content type and source hash, holds IParseRootNode result. Retains most recently - * used ASTs. - */ - private LRUCache fParseCache; private Map parsers; private Map pools; + private final ParsingEngine fParsingEngine; /** * Singleton! @@ -97,7 +89,7 @@ public Set getSupportElementNames() */ private ParserPoolFactory() { - fParseCache = new LRUCache(3); + fParsingEngine = new ParsingEngine(this); } /** @@ -105,11 +97,7 @@ private ParserPoolFactory() */ synchronized void dispose() { - if (fParseCache != null) - { - fParseCache.flush(); - fParseCache = null; - } + fParsingEngine.dispose(); if (pools != null) { @@ -222,7 +210,7 @@ public static IParseRootNode parse(String contentTypeId, String source) throws E */ public static IParseRootNode parse(String contentTypeId, String source, int startingOffset, IProgressMonitor monitor) throws Exception // $codepro.audit.disable - // declaredExceptions + // declaredExceptions { ParseState parseState = new ParseState(); parseState.setEditState(source, startingOffset); @@ -254,68 +242,6 @@ public static IParseRootNode parse(String contentTypeId, IParseState parseState) private IParseRootNode doParse(String contentTypeId, IParseState parseState) throws Exception // $codepro.audit.disable // declaredExceptions { - try - { - if (contentTypeId == null) - { - return null; - } - - int sourceHash = parseState.getSource().hashCode(); - String key = MessageFormat.format("{0}:{1}", contentTypeId, sourceHash); //$NON-NLS-1$ - IParseState cached = fParseCache.get(key); - if (cached != null && !cached.requiresReparse(parseState)) - { - // copy over errors from old parse state to new one since we're not re-parsing - for (IParseError error : cached.getErrors()) - { - parseState.addError(error); - } - return cached.getParseResult(); - } - - IParserPool pool = getParserPool(contentTypeId); - if (pool != null) - { - IParser parser = pool.checkOut(); - - if (parser != null) - { - try - { - IParseRootNode ast = parser.parse(parseState); - parseState.setParseResult(ast); - fParseCache.put(key, parseState); - return ast; - } - finally - { - pool.checkIn(parser); - } - } - else - { - String message = MessageFormat.format(Messages.ParserPoolFactory_Cannot_Acquire_Parser, - contentTypeId); - IdeLog.logError(ParsingPlugin.getDefault(), message, IDebugScopes.PARSING); - } - } - else - { - if (IdeLog.isInfoEnabled(ParsingPlugin.getDefault(), null)) - { - String message = MessageFormat.format(Messages.ParserPoolFactory_Cannot_Acquire_Parser_Pool, - contentTypeId); - IdeLog.logInfo(ParsingPlugin.getDefault(), message, IDebugScopes.PARSING); - } - } - } - finally - { - // Clean up source inside parse state to help reduce RAM usage... - parseState.clearEditState(); - } - - return null; + return fParsingEngine.parse(contentTypeId, parseState); } } diff --git a/plugins/com.aptana.parsing/src/com/aptana/parsing/ParsingEngine.java b/plugins/com.aptana.parsing/src/com/aptana/parsing/ParsingEngine.java new file mode 100644 index 0000000000..54a905a26c --- /dev/null +++ b/plugins/com.aptana.parsing/src/com/aptana/parsing/ParsingEngine.java @@ -0,0 +1,296 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ +package com.aptana.parsing; + +import java.text.MessageFormat; + +import com.aptana.core.epl.util.LRUCache; +import com.aptana.core.logging.IdeLog; +import com.aptana.parsing.ast.IParseRootNode; + +/** + * This class is responsible for actually calling the parsing. It'll use the ParseState#getCacheKey() to know if an + * ongoing parse can be used for a new requestor (and if so, that requestor will be blocked until the end of the parse + * rather than doing the parse itself). + * + * @author Fabio + */ +public class ParsingEngine +{ + + public static interface IParserPoolProvider + { + IParserPool getParserPool(String contentTypeId); + } + + /** + * Internal class to help in the synchronization of the parsing results. + */ + private static class CacheValue + { + /** + * Key for the state. + */ + private IParseStateCacheKey fCachedParseStateKey; + + /** + * This is the state that was used for the parsing. + */ + private IParseState fCachedParseState; + + /** + * Lock to help in synchronizing (threads should wait in it while a result is not available). + */ + private final Object fLock = new Object(); + + /** + * Boolean determining if the results are available or not. Access should be synchronized with fLock. + */ + private volatile boolean fResultGotten = false; + + /** + * @param parseStateKey + * the key for which the parse will be done. + * @param parseState + * the state for which the parse will be done. + */ + public CacheValue(IParseStateCacheKey parseStateKey, IParseState parseState) + { + fCachedParseStateKey = parseStateKey; + fCachedParseState = parseState; + } + + /** + * @return true if the new cache key requires a reparse. + */ + public boolean requiresReparse(IParseStateCacheKey newCacheKey) + { + return fCachedParseStateKey.requiresReparse(newCacheKey); + } + + /** + * @return the result from doing the parse. If it's still not available, blocks until it's provided. + */ + public IParseRootNode getResult(IParseState newParseState) + { + while (!fResultGotten) // Double-check pattern for speed. + { + synchronized (fLock) + { + if (!fResultGotten) + { + try + { + fLock.wait(); + } + catch (InterruptedException e) + { + // ignore + } + } + } + } + newParseState.copyErrorsFrom(fCachedParseState); + return fCachedParseState.getParseResult(); + } + + /** + * Sets the result of the parse. Notifies any waiting thread that it has become available. + */ + public void setResult(IParseRootNode ast) + { + fCachedParseState.setParseResult(ast); + synchronized (fLock) + { + fResultGotten = true; + fLock.notifyAll(); + } + } + } + + /** + * A parse cache. Keyed by combo of content type and source hash, holds IParseRootNode result. Retains most recently + * used ASTs. + */ + private LRUCache fParseCache; + + /** + * Object providing access to the pool provider. + */ + private IParserPoolProvider fParserPoolProvider; + + /** + * Any access to the fParseCache should have this lock in place. + */ + private final Object fParseCacheLock = new Object(); + + public ParsingEngine(IParserPoolProvider parserPoolProvider) + { + fParseCache = new LRUCache(3); + fParserPoolProvider = parserPoolProvider; + } + + public void dispose() + { + fParseCache = null; + } + + /** + * To be used for testing purposes only. + */ + public void clearCache() + { + if (fParseCache == null) // already disposed. + { + return; + } + fParseCache.flush(); + } + + public IParseRootNode parse(String contentTypeId, IParseState parseState) throws Exception // $codepro.audit.disable + // declaredExceptions + { + try + { + if (contentTypeId == null) + { + return null; + } + + String source = parseState.getSource(); + if (source == null) + { + // If we don't have the source, we're not able to do a parse in the first place. + IdeLog.logError(ParsingPlugin.getDefault(), Messages.ParserPoolFactory_Expecting_Source, + IDebugScopes.PARSING); + return null; + } + IParseStateCacheKey newParseStateKey = parseState.getCacheKey(contentTypeId); + CacheValue cacheValue = null; + LRUCache parseCache = fParseCache; + if (parseCache == null) + { + return null; // already disposed. + } + + IParserPool pool = null; + IParser parser = null; + boolean getResultFromCache = false; + + try + { + synchronized (fParseCacheLock) + { + cacheValue = parseCache.get(newParseStateKey); + if (cacheValue != null && !cacheValue.requiresReparse(newParseStateKey)) + { + // Cache hit... it may still be in progress, but the cacheValue.getResult should handle that + // (but we'll get out of the synchronized block to actually do that). + getResultFromCache = true; + } + else + { + // No cache-hit, we'll do the parsing here. + pool = fParserPoolProvider.getParserPool(contentTypeId); + + // If we won't be able to do the parsing because we're unable to get the pool or the + // parser, don't even register the cache value (so that no one listens for something thot + // won't yield a correct return anyways). + if (pool == null) + { + if (IdeLog.isInfoEnabled(ParsingPlugin.getDefault(), null)) + { + String message = MessageFormat.format( + Messages.ParserPoolFactory_Cannot_Acquire_Parser_Pool, contentTypeId); + IdeLog.logInfo(ParsingPlugin.getDefault(), message, IDebugScopes.PARSING); + } + return null; + } + parser = pool.checkOut(); + if (parser == null) + { + String message = MessageFormat.format(Messages.ParserPoolFactory_Cannot_Acquire_Parser, + contentTypeId); + IdeLog.logError(ParsingPlugin.getDefault(), message, IDebugScopes.PARSING); + return null; + } + + // Ok, we're in a state where either there's no one parsing or the currently cached value does + // not match the one in the cache for this key (i.e.: parse without comments and later with + // comments). + cacheValue = new CacheValue(newParseStateKey, parseState); + parseCache.put(newParseStateKey, cacheValue); + // Important: after we put it here (in the situation getResultFromCache), we MUST have a result + // cacheValue.setResult(), otherwise we may end up with a listener waiting eternally for a + // result. + } + } + } + catch (Throwable e) + { + if (!getResultFromCache) + { + // Clean up if something bad happened at somewhere there (to avoid any possible deadlock). + if (pool != null && parser != null) + { + try + { + pool.checkIn(parser); + } + catch (Throwable e1) + { + // Don't even log this one (we're already in a bad state if something happened and we'll + // throw the original exception). + } + } + if (cacheValue != null) + { + // We really HAVE to call this one to avoid possible deadlocks. + cacheValue.setResult(null); + } + } + throw new RuntimeException(e); + + } + + if (getResultFromCache) + { + return cacheValue.getResult(parseState); + } + else + { + IParseRootNode ast = null; + try + { + try + { + ast = parser.parse(parseState); + } + finally + { + pool.checkIn(parser); + } + } + finally + { + // Set the result even if this means setting null (otherwise it's possible that some listener + // deadlocks because of that). + cacheValue.setResult(ast); + } + return ast; + } + } + finally + { + // Clean up source inside parse state to help reduce RAM usage... + parseState.clearEditState(); + } + + } + +} diff --git a/plugins/com.aptana.parsing/src/com/aptana/parsing/messages.properties b/plugins/com.aptana.parsing/src/com/aptana/parsing/messages.properties index d268c44105..118207c0f1 100644 --- a/plugins/com.aptana.parsing/src/com/aptana/parsing/messages.properties +++ b/plugins/com.aptana.parsing/src/com/aptana/parsing/messages.properties @@ -1,2 +1,3 @@ +ParserPoolFactory_Expecting_Source=Expecting source to be available for parsing. ParserPoolFactory_Cannot_Acquire_Parser=Unable to acquire parser for content type: "{0}" ParserPoolFactory_Cannot_Acquire_Parser_Pool=Unable to acquire parser pool for content type: "{0}" diff --git a/tests/com.aptana.core.tests/src/com/aptana/core/util/AllUtilTests.java b/tests/com.aptana.core.tests/src/com/aptana/core/util/AllUtilTests.java index 3c138a0f5f..ffde092af0 100644 --- a/tests/com.aptana.core.tests/src/com/aptana/core/util/AllUtilTests.java +++ b/tests/com.aptana.core.tests/src/com/aptana/core/util/AllUtilTests.java @@ -25,6 +25,7 @@ public static Test suite() suite.addTestSuite(ExpiringMapTests.class); suite.addTestSuite(FileUtilTest.class); suite.addTestSuite(FirefoxUtilTest.class); + suite.addTestSuite(ImmutableTupleNTest.class); suite.addTestSuite(InputStreamGobblerTest.class); suite.addTestSuite(IOUtilTest.class); suite.addTestSuite(ObjectUtilTest.class); diff --git a/tests/com.aptana.core.tests/src/com/aptana/core/util/ImmutableTupleNTest.java b/tests/com.aptana.core.tests/src/com/aptana/core/util/ImmutableTupleNTest.java new file mode 100644 index 0000000000..6dca2219e1 --- /dev/null +++ b/tests/com.aptana.core.tests/src/com/aptana/core/util/ImmutableTupleNTest.java @@ -0,0 +1,47 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ +package com.aptana.core.util; + +import junit.framework.TestCase; + +/** + * @author Fabio + */ +public class ImmutableTupleNTest extends TestCase +{ + + public void testImmutableTuple() throws Exception + { + ImmutableTupleN tuple1 = new ImmutableTupleN(new Object[] { null }); + ImmutableTupleN tuple2 = new ImmutableTupleN(new Object[] { 1, 2 }); + ImmutableTupleN tuple3 = new ImmutableTupleN(new Object[] { 1, "3" }); + ImmutableTupleN tuple4 = new ImmutableTupleN(new Object[] { 1, "3" }); + ImmutableTupleN tuple5 = new ImmutableTupleN(new Object[] { 1, 2, 3 }); + ImmutableTupleN tuple6 = new ImmutableTupleN(new Object[] { 1 }); + + assertEquals(tuple3.hashCode(), tuple4.hashCode()); + assertTrue(tuple3.equals(tuple4)); + + assertFalse(tuple3.equals(tuple1)); + assertFalse(tuple3.equals(tuple2)); + + assertFalse(tuple2.hashCode() == tuple1.hashCode()); + assertFalse("Should not equal!", tuple6.equals(tuple1)); + assertFalse("Should not equal!", tuple1.equals(tuple6)); + + ImmutableTupleN immutableTupleN = new ImmutableTupleN(tuple1, tuple2, tuple3, tuple4, tuple5); + for (int i = 0; i < immutableTupleN.size(); i++) + { + immutableTupleN.getAt(i).toString(); + } + for (int i = 0; i < immutableTupleN.size(); i++) + { + immutableTupleN.getAt(i).hashCode(); + } + } +} diff --git a/tests/com.aptana.index.core.tests/.settings/org.eclipse.jdt.core.prefs b/tests/com.aptana.index.core.tests/.settings/org.eclipse.jdt.core.prefs index fdf2b7e6f2..8155087f6d 100644 --- a/tests/com.aptana.index.core.tests/.settings/org.eclipse.jdt.core.prefs +++ b/tests/com.aptana.index.core.tests/.settings/org.eclipse.jdt.core.prefs @@ -1,4 +1,4 @@ -#Tue Aug 30 12:53:33 EDT 2011 +#Thu Feb 16 09:56:25 EST 2012 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 @@ -6,3 +6,283 @@ org.eclipse.jdt.core.compiler.compliance=1.5 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.source=1.5 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=48 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=next_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=next_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=next_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=next_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=next_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=120 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/tests/com.aptana.index.core.tests/.settings/org.eclipse.jdt.ui.prefs b/tests/com.aptana.index.core.tests/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000000..74ee1e5cdf --- /dev/null +++ b/tests/com.aptana.index.core.tests/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,4 @@ +#Tue Aug 02 13:32:54 PDT 2011 +eclipse.preferences.version=1 +formatter_profile=_Aptana Java Formatting Preferences +formatter_settings_version=12 diff --git a/tests/com.aptana.index.core.tests/src/com/aptana/index/core/build/BuildContextTest.java b/tests/com.aptana.index.core.tests/src/com/aptana/index/core/build/BuildContextTest.java new file mode 100644 index 0000000000..c0a8bd219b --- /dev/null +++ b/tests/com.aptana.index.core.tests/src/com/aptana/index/core/build/BuildContextTest.java @@ -0,0 +1,73 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ +package com.aptana.index.core.build; + +import junit.framework.TestCase; + +import org.eclipse.core.runtime.CoreException; + +import beaver.Symbol; + +import com.aptana.parsing.IParseState; +import com.aptana.parsing.ParseState; +import com.aptana.parsing.ast.IParseRootNode; +import com.aptana.parsing.ast.ParseError; +import com.aptana.parsing.ast.ParseRootNode; + +/** + * @author Fabio + */ +public class BuildContextTest extends TestCase +{ + + public void testBuildContext() throws Exception + { + final int[] reparses = new int[] { 0 }; + final String[] content = new String[] { "" }; + final ParseRootNode parseRootNode = new ParseRootNode("test", new Symbol[0], 0, 0); + BuildContext buildContext = new BuildContext(null) + { + @Override + public String getContentType() throws CoreException + { + return "test"; + } + + @Override + protected IParseRootNode parse(String contentType, IParseState parseState) throws Exception + { + reparses[0] += 1; + parseState.addError(new ParseError("language", new Symbol(1), null)); + return parseRootNode; + } + + @Override + public synchronized String getContents() + { + return content[0]; + } + }; + + ParseState parseState = new ParseState(); + IParseRootNode ast = buildContext.getAST(parseState); + assertEquals(parseRootNode, ast); + assertEquals(1, parseState.getErrors().size()); + assertEquals(1, reparses[0]); + + parseState = new ParseState(); + ast = buildContext.getAST(parseState); // This time it's cached. + assertEquals(1, parseState.getErrors().size()); //errors must be copied + assertEquals(parseRootNode, ast); + assertEquals(1, reparses[0]); + + content[0] = "new"; + ast = buildContext.getAST(); // Not cached again. + assertEquals(parseRootNode, ast); + assertEquals(2, reparses[0]); + } +} diff --git a/tests/com.aptana.index.core.tests/src/com/aptana/index/core/tests/AllIndexCoreTests.java b/tests/com.aptana.index.core.tests/src/com/aptana/index/core/tests/AllIndexCoreTests.java index 3330d39afd..7788486cd2 100644 --- a/tests/com.aptana.index.core.tests/src/com/aptana/index/core/tests/AllIndexCoreTests.java +++ b/tests/com.aptana.index.core.tests/src/com/aptana/index/core/tests/AllIndexCoreTests.java @@ -5,6 +5,7 @@ import junit.framework.TestSuite; import com.aptana.index.core.IndexCoreTests; +import com.aptana.index.core.build.BuildContextTest; import com.aptana.internal.index.core.DiskIndexTest; public class AllIndexCoreTests extends TestCase @@ -15,6 +16,7 @@ public static Test suite() TestSuite suite = new TestSuite(AllIndexCoreTests.class.getName()); // $JUnit-BEGIN$ suite.addTestSuite(DiskIndexTest.class); + suite.addTestSuite(BuildContextTest.class); suite.addTest(IndexCoreTests.suite()); // $JUnit-END$ return suite; diff --git a/tests/com.aptana.parsing.tests/src/com/aptana/parsing/ParseStateCacheKeyWithCommentsTest.java b/tests/com.aptana.parsing.tests/src/com/aptana/parsing/ParseStateCacheKeyWithCommentsTest.java new file mode 100644 index 0000000000..ba7b3af858 --- /dev/null +++ b/tests/com.aptana.parsing.tests/src/com/aptana/parsing/ParseStateCacheKeyWithCommentsTest.java @@ -0,0 +1,32 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ +package com.aptana.parsing; + +import junit.framework.TestCase; + +/** + * @author Fabio + */ +public class ParseStateCacheKeyWithCommentsTest extends TestCase +{ + + public void testParseStateCacheKeyWithCommentsTest() throws Exception + { + ParseStateCacheKeyWithComments p0 = new ParseStateCacheKeyWithComments(true, true, new ParseStateCacheKey()); + ParseStateCacheKeyWithComments p1 = new ParseStateCacheKeyWithComments(true, false, new ParseStateCacheKey()); + ParseStateCacheKeyWithComments p2 = new ParseStateCacheKeyWithComments(true, false, new ParseStateCacheKey( + "test")); + + assertEquals(p0, p1); + assertEquals(p0.hashCode(), p1.hashCode()); + assertFalse("p0 should NOT require a reparse", p0.requiresReparse(p1)); + assertTrue("p1 should require a reparse", p1.requiresReparse(p0)); + + assertFalse(p0.equals(p2)); + } +} diff --git a/tests/com.aptana.parsing.tests/src/com/aptana/parsing/pool/AllTests.java b/tests/com.aptana.parsing.tests/src/com/aptana/parsing/pool/AllTests.java new file mode 100644 index 0000000000..a9ffb8ed59 --- /dev/null +++ b/tests/com.aptana.parsing.tests/src/com/aptana/parsing/pool/AllTests.java @@ -0,0 +1,26 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ +package com.aptana.parsing.pool; + +import junit.framework.Test; +import junit.framework.TestSuite; + +@SuppressWarnings("nls") +public class AllTests +{ + + public static Test suite() + { + TestSuite suite = new TestSuite("Test for com.aptana.parsing.pool"); + //$JUnit-BEGIN$ + suite.addTestSuite(ParsingPoolFactoryTest.class); + //$JUnit-END$ + return suite; + } + +} \ No newline at end of file diff --git a/tests/com.aptana.parsing.tests/src/com/aptana/parsing/pool/ParsingPoolFactoryTest.java b/tests/com.aptana.parsing.tests/src/com/aptana/parsing/pool/ParsingPoolFactoryTest.java new file mode 100644 index 0000000000..317c3936a5 --- /dev/null +++ b/tests/com.aptana.parsing.tests/src/com/aptana/parsing/pool/ParsingPoolFactoryTest.java @@ -0,0 +1,310 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ +package com.aptana.parsing.pool; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import junit.framework.TestCase; +import beaver.Symbol; + +import com.aptana.parsing.IParseState; +import com.aptana.parsing.IParseStateCacheKey; +import com.aptana.parsing.IParser; +import com.aptana.parsing.IParserPool; +import com.aptana.parsing.ParseState; +import com.aptana.parsing.ParseStateCacheKeyWithComments; +import com.aptana.parsing.ParsingEngine; +import com.aptana.parsing.ast.IParseRootNode; +import com.aptana.parsing.ast.ParseRootNode; + +/** + * @author Fabio + */ +public class ParsingPoolFactoryTest extends TestCase +{ + + private final int PARSE_TIMEOUT = 20; + + /** + * @author Fabio + */ + private static final class ParseStateCollectingComments extends ParseState + { + @Override + public IParseStateCacheKey getCacheKey(String contentTypeId) + { + return new ParseStateCacheKeyWithComments(true, true, super.getCacheKey(contentTypeId)); + } + } + + /** + * @author Fabio + */ + private static final class ParseStateNotCollectingComments extends ParseState + { + @Override + public IParseStateCacheKey getCacheKey(String contentTypeId) + { + return new ParseStateCacheKeyWithComments(false, false, super.getCacheKey(contentTypeId)); + } + } + + /** + * @author Fabio + */ + private static final class ParserPoolProvider implements ParsingEngine.IParserPoolProvider + { + /** + * + */ + private final IParserPool parserPool; + + /** + * @param parserPool + */ + private ParserPoolProvider(IParserPool parserPool) + { + this.parserPool = parserPool; + } + + public IParserPool getParserPool(String contentTypeId) + { + if (!contentTypeId.equals("test")) + { + fail("Expected content type to be 'test'"); + } + return parserPool; + } + } + + /** + * @author Fabio + */ + private static final class ParserPool implements IParserPool + { + /** + * + */ + private final Parser parser; + + /** + * @param parser + */ + private ParserPool(Parser parser) + { + this.parser = parser; + } + + public boolean validate(IParser o) + { + throw new RuntimeException("Not implemented"); + } + + public void expire(IParser o) + { + throw new RuntimeException("Not implemented"); + } + + public void dispose() + { + throw new RuntimeException("Not implemented"); + } + + public IParser create() + { + throw new RuntimeException("Not implemented"); + } + + public IParser checkOut() + { + return parser; + } + + public void checkIn(IParser t) + { + } + } + + /** + * @author Fabio + */ + private static final class Parser implements IParser + { + /** + * + */ + private final Queue queue; + + public int parseTimeout; + + public int parses; + + /** + * @param queue + */ + private Parser(Queue queue) + { + this.queue = queue; + } + + public synchronized IParseRootNode parse(IParseState parseState) throws Exception + { + Thread.sleep(parseTimeout); + parses += 1; + return queue.remove(); + } + } + + Queue queue; + + Parser parser; + + IParserPool parserPool; + + ParsingEngine parsingEngine; + + ParseRootNode parseRootNode; + + protected void setUp() throws Exception + { + + queue = new ConcurrentLinkedQueue(); + + parser = new Parser(queue); + + parserPool = new ParserPool(parser); + + parsingEngine = new ParsingEngine(new ParserPoolProvider(parserPool)); + + parseRootNode = new ParseRootNode("test", new Symbol[0], 0, 0); + }; + + public void testParserPoolFactory() throws Exception + { + queue.add(parseRootNode); + IParseRootNode ast = parsingEngine.parse("test", new ParseState()); + assertEquals(parseRootNode, ast); + assertEquals(0, queue.size()); + assertEquals(1, parser.parses); + + // Second parse: ast should be cached. + ast = parsingEngine.parse("test", new ParseState()); + assertEquals(parseRootNode, ast); + assertEquals(0, queue.size()); + assertEquals(1, parser.parses); + } + + public void testParserPoolFactoryThreaded() throws Exception + { + + parser.parseTimeout = PARSE_TIMEOUT; + queue.add(parseRootNode); + final Queue exceptions = new ConcurrentLinkedQueue(); + final Queue asts = new ConcurrentLinkedQueue(); + + Runnable r = new Runnable() + { + + public void run() + { + ParseState parseState = new ParseState(); + try + { + IParseRootNode node = parsingEngine.parse("test", parseState); + if (node == null) + { + return; + } + asts.add(node); + } + catch (Exception e) + { + exceptions.add(e); + } + + } + }; + parsingEngine.clearCache(); + + int expectedParses = 100; + + for (int j = 0; j < expectedParses; j++) + { + new Thread(r).start(); + } + for (int i = 0; i < 100; i++) + { + if (exceptions.size() > 0) + { + throw exceptions.poll(); + } + if (asts.size() == expectedParses) + { + break; + } + Thread.sleep(PARSE_TIMEOUT); + } + // Check if we really got to the proper state + assertEquals(1, parser.parses); + assertEquals(expectedParses, asts.size()); + if (exceptions.size() > 0) + { + throw exceptions.poll(); + } + r.run(); + assertEquals(1, parser.parses); + assertEquals(expectedParses + 1, asts.size()); + if (exceptions.size() > 0) + { + throw exceptions.poll(); + } + + parsingEngine.dispose(); + r.run(); + assertEquals(1, parser.parses); + assertEquals(expectedParses + 1, asts.size()); + if (exceptions.size() > 0) + { + throw exceptions.poll(); + } + } + + public void testParseStateWithComments() throws Exception + { + queue.add(parseRootNode); + + IParseRootNode ast = parsingEngine.parse("test", new ParseStateCollectingComments()); + assertEquals(parseRootNode, ast); + assertEquals(0, queue.size()); + assertEquals(1, parser.parses); + + // Second parse: ast should be cached as the first has the comments. + ast = parsingEngine.parse("test", new ParseStateNotCollectingComments()); + assertEquals(parseRootNode, ast); + assertEquals(0, queue.size()); + assertEquals(1, parser.parses); + + parsingEngine.clearCache(); + + queue.add(parseRootNode); + + ast = parsingEngine.parse("test", new ParseStateNotCollectingComments()); + assertEquals(parseRootNode, ast); + assertEquals(0, queue.size()); + assertEquals(2, parser.parses); + + queue.add(parseRootNode); + // Second parse: this time as it was cached without comments, it should be reparsed. + ast = parsingEngine.parse("test", new ParseStateCollectingComments()); + assertEquals(parseRootNode, ast); + assertEquals(0, queue.size()); + assertEquals(3, parser.parses); + } + +} diff --git a/tests/com.aptana.parsing.tests/src/com/aptana/parsing/tests/AllTests.java b/tests/com.aptana.parsing.tests/src/com/aptana/parsing/tests/AllTests.java index 21248ca9ef..fdd92d6856 100644 --- a/tests/com.aptana.parsing.tests/src/com/aptana/parsing/tests/AllTests.java +++ b/tests/com.aptana.parsing.tests/src/com/aptana/parsing/tests/AllTests.java @@ -7,6 +7,8 @@ */ package com.aptana.parsing.tests; +import com.aptana.parsing.ParseStateCacheKeyWithCommentsTest; + import junit.framework.Test; import junit.framework.TestResult; import junit.framework.TestSuite; @@ -27,9 +29,12 @@ public void runTest(Test test, TestResult result) } }; // $JUnit-BEGIN$ + suite.addTestSuite(ParseStateCacheKeyWithCommentsTest.class); + suite.addTestSuite(ParseStateTest.class); suite.addTest(com.aptana.json.AllTests.suite()); suite.addTest(com.aptana.parsing.ast.AllTests.suite()); suite.addTest(com.aptana.parsing.lexer.LexerTests.suite()); + suite.addTest(com.aptana.parsing.pool.AllTests.suite()); suite.addTest(com.aptana.sax.AllTests.suite()); // $JUnit-END$ return suite; diff --git a/tests/com.aptana.parsing.tests/src/com/aptana/parsing/tests/ParseStateTest.java b/tests/com.aptana.parsing.tests/src/com/aptana/parsing/tests/ParseStateTest.java new file mode 100644 index 0000000000..c7e386b83d --- /dev/null +++ b/tests/com.aptana.parsing.tests/src/com/aptana/parsing/tests/ParseStateTest.java @@ -0,0 +1,51 @@ +/** + * Aptana Studio + * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. + * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). + * Please see the license.html included with this distribution for details. + * Any modifications to this file must keep this entire header intact. + */ +package com.aptana.parsing.tests; + +import com.aptana.parsing.ParseState; + +import junit.framework.TestCase; + +/** + * @author Fabio + */ +public class ParseStateTest extends TestCase +{ + + public void testParseState() throws Exception + { + ParseState parseState = new ParseState(); + parseState.setEditState("test"); + Object key1 = parseState.getCacheKey("contentType"); + + parseState = new ParseState(); + parseState.setEditState("test"); + Object key1a = parseState.getCacheKey("contentType"); + + parseState = new ParseState(); + parseState.setEditState("test1234569078000-"); + Object key2 = parseState.getCacheKey("contentType"); + + parseState = new ParseState(); + parseState.setEditState("test1234569078000-"); + Object key2a = parseState.getCacheKey("contentType"); + + parseState = new ParseState(); + parseState.setEditState("test1234569078000"); + Object key3 = parseState.getCacheKey("contentType"); + + assertEquals(key1, key1a); + assertEquals(key2, key2a); + assertEquals(key1.hashCode(), key1a.hashCode()); + assertEquals(key2.hashCode(), key2a.hashCode()); + + assertFalse(key1.equals(key2)); + assertFalse(key3.equals(key2)); + assertFalse(key3.equals(key1)); + } +}