0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.cnd.modelimpl.csm.core;
0043:
0044: import antlr.Token;
0045: import antlr.TokenStream;
0046: import antlr.TokenStreamException;
0047: import antlr.collections.AST;
0048: import java.util.concurrent.CopyOnWriteArrayList;
0049: import java.util.concurrent.locks.ReentrantReadWriteLock;
0050: import org.netbeans.modules.cnd.api.model.*;
0051: import org.netbeans.modules.cnd.api.model.util.CsmTracer;
0052: import org.netbeans.modules.cnd.apt.support.APTHandlersSupport;
0053: import org.netbeans.modules.cnd.modelimpl.debug.Diagnostic;
0054: import org.netbeans.modules.cnd.modelimpl.debug.TraceFlags;
0055: import org.netbeans.modules.cnd.modelimpl.parser.CPPParserEx;
0056:
0057: import java.io.*;
0058: import java.lang.ref.Reference;
0059: import java.lang.ref.SoftReference;
0060: import java.util.*;
0061: import java.util.concurrent.locks.ReadWriteLock;
0062: import java.util.logging.Level;
0063: import org.netbeans.modules.cnd.apt.support.APTLanguageFilter;
0064: import org.netbeans.modules.cnd.apt.support.APTLanguageSupport;
0065: import org.netbeans.modules.cnd.modelimpl.cache.CacheManager;
0066: import org.netbeans.modules.cnd.modelimpl.cache.FileCache;
0067: import org.netbeans.modules.cnd.modelimpl.cache.impl.FileCacheImpl;
0068: import org.netbeans.modules.cnd.modelimpl.csm.*;
0069: import javax.swing.event.ChangeListener;
0070: import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities;
0071: import org.netbeans.modules.cnd.api.project.NativeFileItem;
0072: import org.netbeans.modules.cnd.apt.structure.APTFile;
0073: import org.netbeans.modules.cnd.apt.support.APTDriver;
0074: import org.netbeans.modules.cnd.apt.support.APTPreprocHandler;
0075: import org.netbeans.modules.cnd.apt.support.APTToken;
0076: import org.netbeans.modules.cnd.apt.support.StartEntry;
0077: import org.netbeans.modules.cnd.apt.utils.APTUtils;
0078: import org.netbeans.modules.cnd.modelimpl.debug.DiagnosticExceptoins;
0079: import org.netbeans.modules.cnd.modelimpl.parser.apt.APTParseFileWalker;
0080: import org.netbeans.modules.cnd.modelimpl.parser.apt.GuardBlockWalker;
0081: import org.netbeans.modules.cnd.modelimpl.parser.generated.CPPTokenTypes;
0082: import org.netbeans.modules.cnd.modelimpl.platform.ModelSupport;
0083: import org.netbeans.modules.cnd.modelimpl.repository.PersistentUtils;
0084: import org.netbeans.modules.cnd.modelimpl.repository.RepositoryUtils;
0085: import org.netbeans.modules.cnd.modelimpl.textcache.NameCache;
0086: import org.netbeans.modules.cnd.modelimpl.uid.LazyCsmCollection;
0087: import org.netbeans.modules.cnd.modelimpl.uid.UIDCsmConverter;
0088: import org.netbeans.modules.cnd.modelimpl.uid.UIDObjectFactory;
0089: import org.netbeans.modules.cnd.modelimpl.uid.UIDUtilities;
0090: import org.netbeans.modules.cnd.repository.spi.Persistent;
0091: import org.netbeans.modules.cnd.repository.support.SelfPersistent;
0092: import org.netbeans.modules.cnd.utils.cache.CharSequenceKey;
0093:
0094: /**
0095: * CsmFile implementations
0096: * @author Vladimir Kvashin
0097: */
0098: public class FileImpl implements CsmFile, MutableDeclarationsContainer,
0099: ChangeListener, Disposable, Persistent, SelfPersistent {
0100:
0101: public static final boolean reportErrors = TraceFlags.REPORT_PARSING_ERRORS
0102: | TraceFlags.DEBUG;
0103: private static final boolean reportParse = Boolean
0104: .getBoolean("parser.log.parse");
0105:
0106: private static final boolean emptyAstStatictics = Boolean
0107: .getBoolean("parser.empty.ast.statistics");
0108:
0109: private static final boolean SKIP_UNNECESSARY_FAKE_FIXES = false;
0110:
0111: public static final int UNDEFINED_FILE = 0;
0112: public static final int SOURCE_FILE = 1;
0113: public static final int SOURCE_C_FILE = 2;
0114: public static final int SOURCE_CPP_FILE = 3;
0115: public static final int HEADER_FILE = 4;
0116:
0117: private FileBuffer fileBuffer;
0118:
0119: // only one of project/projectUID must be used (based on USE_UID_TO_CONTAINER)
0120: private/*final*/ProjectBase projectRef;// can be set in onDispose or contstructor only
0121: private final CsmUID<CsmProject> projectUID;
0122:
0123: /**
0124: * It's a map since we need to eliminate duplications
0125: */
0126: private Map<SortedKey, CsmUID<CsmOffsetableDeclaration>> declarations = new TreeMap<SortedKey, CsmUID<CsmOffsetableDeclaration>>();
0127: private ReadWriteLock declarationsLock = new ReentrantReadWriteLock();
0128:
0129: private Set<CsmUID<CsmInclude>> includes = createIncludes();
0130: private ReadWriteLock includesLock = new ReentrantReadWriteLock();
0131:
0132: private Set<CsmUID<CsmMacro>> macros = createMacros();
0133: private ReadWriteLock macrosLock = new ReentrantReadWriteLock();
0134:
0135: private int errorCount = 0;
0136:
0137: private enum State {
0138: INITIAL, PARSED, MODIFIED, BEING_PARSED
0139: }
0140:
0141: private State state;
0142:
0143: private int fileType = UNDEFINED_FILE;
0144:
0145: private Object stateLock = new Object();
0146:
0147: private Collection<FunctionImplEx> fakeRegistrationsOLD = new ArrayList<FunctionImplEx>();
0148: private Collection<CsmUID<FunctionImplEx>> fakeRegistrationUIDs = new CopyOnWriteArrayList<CsmUID<FunctionImplEx>>();
0149:
0150: // TODO: move this field and correspondent logic to FileContainer.MyFile
0151: private final GuardBlockState guardState;
0152:
0153: private long lastParsed = Long.MIN_VALUE;
0154:
0155: /** Cache the hash code */
0156: private int hash; // Default to 0
0157:
0158: /**
0159: * Stores the UIDs of the static functions declarations (not definitions)
0160: * This is necessary for finding definitions/declarations
0161: * since file-level static functions (i.e. c-style static functions) aren't registered in project
0162: */
0163: private Collection<CsmUID<CsmFunction>> staticFunctionDeclarationUIDs;
0164:
0165: /** For test purposes only */
0166: public interface Hook {
0167: void parsingFinished(CsmFile file,
0168: APTPreprocHandler preprocHandler);
0169: }
0170:
0171: private static Hook hook = null;
0172:
0173: public FileImpl(FileBuffer fileBuffer, ProjectBase project,
0174: int fileType, NativeFileItem nativeFileItem) {
0175: state = State.INITIAL;
0176: setBuffer(fileBuffer);
0177: this .projectUID = UIDCsmConverter.projectToUID(project);
0178: this .projectRef = null;
0179: this .fileType = fileType;
0180: this .guardState = new GuardBlockState();
0181: if (nativeFileItem != null) {
0182: project.putNativeFileItem(getUID(), nativeFileItem);
0183: }
0184: Notificator.instance().registerNewFile(this );
0185: }
0186:
0187: /** For test purposes only */
0188: public static void setHook(Hook aHook) {
0189: hook = aHook;
0190: }
0191:
0192: public final NativeFileItem getNativeFileItem() {
0193: return getProjectImpl().getNativeFileItem(getUID());
0194: }
0195:
0196: private ProjectBase _getProject(boolean assertNotNull) {
0197: ProjectBase prj = this .projectRef;
0198: if (prj == null) {
0199: prj = (ProjectBase) UIDCsmConverter
0200: .UIDtoProject(this .projectUID);
0201: if (assertNotNull) {
0202: assert (prj != null || this .projectUID == null) : "empty project for UID "
0203: + this .projectUID;
0204: }
0205: }
0206: return prj;
0207: }
0208:
0209: public boolean isSourceFile() {
0210: return fileType == SOURCE_FILE || fileType == SOURCE_C_FILE
0211: || fileType == SOURCE_CPP_FILE;
0212: }
0213:
0214: public boolean isCppFile() {
0215: return fileType == SOURCE_CPP_FILE;
0216: }
0217:
0218: /*package local*/void setSourceFile() {
0219: if (!(fileType == SOURCE_C_FILE || fileType == SOURCE_CPP_FILE)) {
0220: fileType = SOURCE_FILE;
0221: }
0222: }
0223:
0224: public boolean isHeaderFile() {
0225: return fileType == HEADER_FILE;
0226: }
0227:
0228: /*package local*/void setHeaderFile() {
0229: if (fileType == UNDEFINED_FILE) {
0230: fileType = HEADER_FILE;
0231: }
0232: }
0233:
0234: // TODO: consider using macro map and __cplusplus here instead of just checking file name
0235: public APTLanguageFilter getLanguageFilter() {
0236: String lang = APTLanguageSupport.GNU_CPP;
0237: String name = getName().toString();
0238:
0239: if (name.length() > 2 && name.endsWith(".c")) { // NOI18N
0240: lang = APTLanguageSupport.GNU_C;
0241: }
0242:
0243: return APTLanguageSupport.getInstance().getFilter(lang);
0244: }
0245:
0246: public APTPreprocHandler getPreprocHandler() {
0247: return getProjectImpl() == null ? null : getProjectImpl()
0248: .getPreprocHandler(fileBuffer.getFile());
0249: }
0250:
0251: public void setBuffer(FileBuffer fileBuffer) {
0252: synchronized (changeStateLock) {
0253: if (this .fileBuffer != null) {
0254: this .fileBuffer.removeChangeListener(this );
0255: }
0256: this .fileBuffer = fileBuffer;
0257: if (state != State.INITIAL) {
0258: state = State.MODIFIED;
0259: }
0260: this .fileBuffer.addChangeListener(this );
0261: }
0262: }
0263:
0264: public FileBuffer getBuffer() {
0265: return this .fileBuffer;
0266: }
0267:
0268: public void ensureParsed(APTPreprocHandler preprocHandler) {
0269: synchronized (stateLock) {
0270: switch (state) {
0271: case INITIAL:
0272: parse(preprocHandler);
0273: if (TraceFlags.DUMP_PARSE_RESULTS)
0274: new CsmTracer().dumpModel(this );
0275: break;
0276: case MODIFIED:
0277: reparse(preprocHandler);
0278: if (TraceFlags.DUMP_PARSE_RESULTS
0279: || TraceFlags.DUMP_REPARSE_RESULTS)
0280: new CsmTracer().dumpModel(this );
0281: break;
0282: case PARSED:
0283: break;
0284: }
0285: }
0286: }
0287:
0288: public boolean validate() {
0289: synchronized (changeStateLock) {
0290: if (state == State.PARSED) {
0291: long lastModified = getBuffer().lastModified();
0292: if (lastModified > lastParsed) {
0293: if (TraceFlags.TRACE_VALIDATION)
0294: System.err
0295: .printf(
0296: "VALIDATED %s\n\t lastModified=%d\n\t lastParsed=%d\n",
0297: getAbsolutePath(),
0298: lastModified, lastParsed);
0299: state = State.MODIFIED;
0300: return false;
0301: }
0302: }
0303: return true;
0304: }
0305: }
0306:
0307: private Object changeStateLock = new Object();
0308:
0309: public void stateChanged(javax.swing.event.ChangeEvent e) {
0310: stateChanged(false);
0311: }
0312:
0313: public void stateChanged(boolean invalidateCache) {
0314: synchronized (changeStateLock) {
0315: if (state != State.INITIAL) {
0316: state = State.MODIFIED;
0317: }
0318: if (invalidateCache) {
0319: synchronized (tokStreamLock) {
0320: ref = null;
0321: }
0322: if (TraceFlags.USE_AST_CACHE) {
0323: CacheManager.getInstance().invalidate(this );
0324: } else {
0325: APTDriver.getInstance().invalidateAPT(
0326: this .getBuffer());
0327: }
0328: }
0329: }
0330: }
0331:
0332: public int getErrorCount() {
0333: return errorCount;
0334: }
0335:
0336: /**
0337: * sometimes called externally
0338: * by some (cached) project implementations, etc
0339: */
0340: public void render(AST tree) {
0341: new AstRenderer(this ).render(tree);
0342: }
0343:
0344: /**
0345: * Removes old content from te file and model,
0346: * then parses the current buffer
0347: */
0348: public void reparse(APTPreprocHandler preprocHandler) {
0349: synchronized (stateLock) {
0350: state = State.BEING_PARSED;
0351: try {
0352: _reparse((preprocHandler == null) ? getPreprocHandler()
0353: : preprocHandler);
0354: } finally {
0355: synchronized (changeStateLock) {
0356: if (state != State.MODIFIED) {
0357: state = State.PARSED;
0358: }
0359: }
0360: stateLock.notifyAll();
0361: }
0362: }
0363: }
0364:
0365: private void _reparse(APTPreprocHandler preprocHandler) {
0366: if (!ParserThreadManager.instance().isParserThread()
0367: && !ParserThreadManager.instance().isStandalone()) {
0368: String text = "Reparsing should be done only in a special Code Model Thread!!!"; // NOI18N
0369: Diagnostic.trace(text);
0370: new Throwable(text).printStackTrace(System.err);
0371: }
0372: if (TraceFlags.DEBUG)
0373: Diagnostic.trace("------ reparsing "
0374: + fileBuffer.getFile().getName()); // NOI18N
0375: //Notificator.instance().startTransaction();
0376: try {
0377: _clearIncludes();
0378: _clearMacros();
0379: AST ast = doParse(preprocHandler);
0380: if (ast != null) {
0381: disposeAll(false);
0382: render(ast);
0383: } else {
0384: //System.err.println("null ast for file " + getAbsolutePath());
0385: }
0386: } finally {
0387: //Notificator.instance().endTransaction();
0388: // update this file and it's project
0389: RepositoryUtils.put(this );
0390: if (TraceFlags.USE_DEEP_REPARSING) {
0391: getProjectImpl().getGraph().putFile(this );
0392: }
0393: Notificator.instance().registerChangedFile(this );
0394: Notificator.instance().flush();
0395: }
0396:
0397: }
0398:
0399: public void dispose() {
0400: onDispose();
0401: Notificator.instance().registerRemovedFile(this );
0402: disposeAll(true);
0403: }
0404:
0405: public void onProjectDispose() {
0406: onDispose();
0407: }
0408:
0409: private void onDispose() {
0410: if (TraceFlags.RESTORE_CONTAINER_FROM_UID) {
0411: // restore container from it's UID
0412: this .projectRef = (ProjectBase) UIDCsmConverter
0413: .UIDtoProject(this .projectUID);
0414: assert (this .projectRef != null || this .projectUID == null) : "empty project for UID "
0415: + this .projectUID;
0416: }
0417: }
0418:
0419: private void disposeAll(boolean clearNonDisposable) {
0420: //NB: we're copying declarations, because dispose can invoke this.removeDeclaration
0421: //for( Iterator iter = declarations.values().iterator(); iter.hasNext(); ) {
0422: Collection<CsmUID<CsmOffsetableDeclaration>> uids;
0423: try {
0424: declarationsLock.writeLock().lock();
0425: uids = declarations.values();
0426: declarations = new TreeMap<SortedKey, CsmUID<CsmOffsetableDeclaration>>();
0427: } finally {
0428: declarationsLock.writeLock().unlock();
0429: }
0430:
0431: if (clearNonDisposable) {
0432: _clearIncludes();
0433: _clearMacros();
0434: }
0435: Collection<CsmOffsetableDeclaration> arr = UIDCsmConverter
0436: .UIDsToDeclarations(uids);
0437: Utils.disposeAll(arr);
0438: RepositoryUtils.remove(uids);
0439: }
0440:
0441: private void _clearMacros() {
0442: Set<CsmUID<CsmMacro>> copy = macros;
0443: macros = createMacros();
0444: RepositoryUtils.remove(copy);
0445: }
0446:
0447: private Set<CsmUID<CsmMacro>> createMacros() {
0448: return new TreeSet<CsmUID<CsmMacro>>(
0449: UID_START_OFFSET_COMPARATOR);
0450: }
0451:
0452: private void _clearIncludes() {
0453: try {
0454: includesLock.writeLock().lock();
0455: RepositoryUtils.remove(includes);
0456: includes = createIncludes();
0457: } finally {
0458: includesLock.writeLock().unlock();
0459: }
0460: }
0461:
0462: private Set<CsmUID<CsmInclude>> createIncludes() {
0463: return new TreeSet<CsmUID<CsmInclude>>(
0464: UID_START_OFFSET_COMPARATOR);
0465: }
0466:
0467: public AST parse(APTPreprocHandler preprocHandler) {
0468: synchronized (stateLock) {
0469: state = State.BEING_PARSED;
0470: try {
0471: return _parse((preprocHandler == null) ? getPreprocHandler()
0472: : preprocHandler);
0473: } finally {
0474: synchronized (changeStateLock) {
0475: if (state != State.MODIFIED) {
0476: state = State.PARSED;
0477: }
0478: }
0479: stateLock.notifyAll();
0480: }
0481: }
0482: }
0483:
0484: private AST _parse(APTPreprocHandler preprocHandler) {
0485:
0486: if (reportErrors) {
0487: if (!ParserThreadManager.instance().isParserThread()
0488: && !ParserThreadManager.instance().isStandalone()) {
0489: String text = "Reparsing should be done only in a special Code Model Thread!!!"; // NOI18N
0490: Diagnostic.trace(text);
0491: new Throwable(text).printStackTrace(System.err);
0492: }
0493: }
0494:
0495: Diagnostic.StopWatch sw = TraceFlags.TIMING_PARSE_PER_FILE_DEEP ? new Diagnostic.StopWatch()
0496: : null;
0497:
0498: try {
0499: AST ast = doParse((preprocHandler == null) ? getPreprocHandler()
0500: : preprocHandler);
0501: if (TraceFlags.TIMING_PARSE_PER_FILE_DEEP)
0502: sw.stopAndReport("Parsing of "
0503: + fileBuffer.getFile().getName() + " took \t"); // NOI18N
0504:
0505: if (ast != null) {
0506: Diagnostic.StopWatch sw2 = TraceFlags.TIMING_PARSE_PER_FILE_DEEP ? new Diagnostic.StopWatch()
0507: : null;
0508: //Notificator.instance().startTransaction();
0509: if (isValid()) { // FIXUP: use a special lock here
0510: render(ast);
0511: if (TraceFlags.TIMING_PARSE_PER_FILE_DEEP)
0512: sw2.stopAndReport("Rendering of "
0513: + fileBuffer.getFile().getName()
0514: + " took \t"); // NOI18N
0515: }
0516: return ast;
0517: }
0518: } finally {
0519: if (isValid()) { // FIXUP: use a special lock here
0520: RepositoryUtils.put(this );
0521: }
0522: if (TraceFlags.USE_DEEP_REPARSING && isValid()) { // FIXUP: use a special lock here
0523: getProjectImpl().getGraph().putFile(this );
0524: }
0525: if (isValid()) { // FIXUP: use a special lock here
0526: Notificator.instance().registerChangedFile(this );
0527: Notificator.instance().flush();
0528: } else {
0529: // FIXUP: there should be a notificator per project instead!
0530: Notificator.instance().reset();
0531: }
0532: }
0533: return null;
0534: }
0535:
0536: private TokenStream createFullTokenStream() {
0537: APTPreprocHandler preprocHandler = getPreprocHandler();
0538: APTFile apt = null;
0539: if (TraceFlags.USE_AST_CACHE) {
0540: apt = CacheManager.getInstance().findAPT(this );
0541: } else {
0542: try {
0543: apt = APTDriver.getInstance().findAPT(fileBuffer);
0544: } catch (IOException ex) {
0545: DiagnosticExceptoins.register(ex);
0546: }
0547: }
0548: if (apt == null) {
0549: return null;
0550: }
0551: ProjectBase startProject = ProjectBase
0552: .getStartProject(preprocHandler.getState());
0553: if (startProject == null) {
0554: System.err.println(" null project for "
0555: + APTHandlersSupport
0556: .extractStartEntry(preprocHandler
0557: .getState())
0558: + // NOI18N
0559: "\n while getting TS of file " + getAbsolutePath()
0560: + "\n of project " + getProject()); // NOI18N
0561: return null;
0562: }
0563: APTParseFileWalker walker = new APTParseFileWalker(
0564: startProject, apt, this , preprocHandler);
0565: return walker.getFilteredTokenStream(getLanguageFilter());
0566: }
0567:
0568: private final String tokStreamLock = new String("TokenStream lock");
0569: private Reference<OffsetTokenStream> ref = new SoftReference(null);
0570:
0571: public TokenStream getTokenStream(int startOffset, int endOffset) {
0572: try {
0573: OffsetTokenStream stream;
0574: synchronized (tokStreamLock) {
0575: stream = ref != null ? ref.get() : null;
0576: ref = new SoftReference(null);
0577: }
0578: if (stream == null || stream.getStartOffset() > startOffset) {
0579: if (stream == null) {
0580: // System.err.println("new stream created for " + startOffset);
0581: } else {
0582: // System.err.println("new stream created, because prev stream was finished on " + stream.getStartOffset() + " now asked for " + startOffset);
0583: }
0584: stream = new OffsetTokenStream(createFullTokenStream());
0585: } else {
0586: // System.err.println("use cached stream finished previously on " + stream.getStartOffset() + " now asked for " + startOffset);
0587: }
0588: stream.moveTo(startOffset, endOffset);
0589: return stream;
0590: } catch (TokenStreamException ex) {
0591: Utils.LOG.severe("Can't create compound statement: "
0592: + ex.getMessage());
0593: DiagnosticExceptoins.register(ex);
0594: return null;
0595: }
0596: }
0597:
0598: public void releaseTokenStream(TokenStream ts) {
0599: if (ts instanceof OffsetTokenStream) {
0600: OffsetTokenStream offsTS = (OffsetTokenStream) ts;
0601: synchronized (tokStreamLock) {
0602: if (ref != null && ref.get() == null) {
0603: ref = new SoftReference<OffsetTokenStream>(offsTS);
0604: // System.err.println("caching stream finished on " + offsTS.getStartOffset());
0605: }
0606: }
0607: }
0608: }
0609:
0610: private static class OffsetTokenStream implements TokenStream {
0611:
0612: private final TokenStream stream;
0613: private Token next;
0614: private int endOffset;
0615:
0616: public OffsetTokenStream(TokenStream stream) {
0617: this .stream = stream;
0618: }
0619:
0620: public Token nextToken() throws TokenStreamException {
0621: Token out = next;
0622:
0623: if (out == null || out.getType() == CPPTokenTypes.EOF
0624: || (((APTToken) out).getOffset() > endOffset)) {
0625: out = APTUtils.EOF_TOKEN;
0626: } else {
0627: next = stream.nextToken();
0628: }
0629: return out;
0630: }
0631:
0632: public int getStartOffset() {
0633: return next == null
0634: || (next.getType() == CPPTokenTypes.EOF) ? Integer.MAX_VALUE
0635: : ((APTToken) next).getOffset();
0636: }
0637:
0638: public void moveTo(int startOffset, int endOffset)
0639: throws TokenStreamException {
0640: this .endOffset = endOffset;
0641: assert this .endOffset >= startOffset;
0642: for (next = stream.nextToken(); next != null
0643: && next.getType() != CPPTokenTypes.EOF; next = stream
0644: .nextToken()) {
0645: assert (next instanceof APTToken) : "we have only APTTokens in token stream";
0646: int currOffset = ((APTToken) next).getOffset();
0647: if (currOffset == startOffset) {
0648: break;
0649: }
0650: }
0651: }
0652: };
0653:
0654: private AST doParse(APTPreprocHandler preprocHandler) {
0655: // if( "cursor.hpp".equals(fileBuffer.getFile().getName()) ) {
0656: // System.err.println("cursor.hpp");
0657: // }
0658: if (reportParse || TraceFlags.DEBUG) {
0659: System.err.println("# APT-based AST-cached Parsing "
0660: + fileBuffer.getFile().getPath() + " (Thread="
0661: + Thread.currentThread().getName() + ')');
0662: }
0663:
0664: int flags = CPPParserEx.CPP_CPLUSPLUS;
0665: if (!reportErrors) {
0666: flags |= CPPParserEx.CPP_SUPPRESS_ERRORS;
0667: }
0668:
0669: APTPreprocHandler.State oldState = preprocHandler.getState();
0670:
0671: // 1. get cache with AST
0672: // 2a if cache has AST => use AST and APTLight
0673: // 2b otherwise if cache has APT full => use APT full to generate parser's
0674: // token stream and save in cache
0675: AST ast = null;
0676: APTFile aptLight = null;
0677: APTFile aptFull = null;
0678: if (TraceFlags.USE_AST_CACHE) {
0679: FileCache cacheWithAST = CacheManager.getInstance()
0680: .findCacheWithAST(this , preprocHandler);
0681: assert (cacheWithAST != null);
0682: ast = cacheWithAST.getAST(preprocHandler);
0683: aptLight = cacheWithAST.getAPTLight();
0684: aptFull = cacheWithAST.getAPT();
0685: } else {
0686: try {
0687: aptFull = APTDriver.getInstance().findAPT(
0688: this .getBuffer());
0689: } catch (FileNotFoundException ex) {
0690: APTUtils.LOG.log(Level.WARNING,
0691: "FileImpl: file {0} not found",
0692: new Object[] { getBuffer().getFile()
0693: .getAbsolutePath() });// NOI18N
0694: DiagnosticExceptoins.register(ex);
0695: } catch (IOException ex) {
0696: DiagnosticExceptoins.register(ex);
0697: }
0698: }
0699: if (ast != null) {
0700: if (TraceFlags.TRACE_CACHE) {
0701: System.err
0702: .println("CACHE: parsing using AST and APTLight for "
0703: + getAbsolutePath());
0704: }
0705: // use light for visiting and return ast as result
0706: assert (aptLight != null);
0707: boolean skip = TraceFlags.CACHE_SKIP_APT_VISIT;
0708: if (!skip) {
0709: APTParseFileWalker walker = new APTParseFileWalker(
0710: ProjectBase.getStartProject(preprocHandler
0711: .getState()), aptLight, this ,
0712: preprocHandler);
0713: walker.addMacroAndIncludes(true);
0714: walker.visit();
0715: } else {
0716: if (TraceFlags.TRACE_CACHE) {
0717: System.err
0718: .println("CACHE: skipped APTLight visiting");
0719: }
0720: }
0721: } else if (aptFull != null) {
0722: // use full APT for generating token stream
0723: if (TraceFlags.TRACE_CACHE) {
0724: System.err.println("CACHE: parsing using full APT for "
0725: + getAbsolutePath());
0726: }
0727: // set guard info
0728: updateGuardAfterParse(preprocHandler, aptFull);
0729: // make real parse
0730: ProjectBase startProject = ProjectBase
0731: .getStartProject(preprocHandler.getState());
0732: if (startProject == null) {
0733: System.err.println(" null project for "
0734: + APTHandlersSupport
0735: .extractStartEntry(preprocHandler
0736: .getState())
0737: + // NOI18N
0738: "\n while parsing file " + getAbsolutePath()
0739: + "\n of project " + getProject()); // NOI18N
0740: return null;
0741: }
0742: APTParseFileWalker walker = new APTParseFileWalker(
0743: startProject, aptFull, this , preprocHandler);
0744: walker.addMacroAndIncludes(true);
0745: if (TraceFlags.DEBUG) {
0746: System.err.println("doParse " + getAbsolutePath()
0747: + " with "
0748: + ParserQueue.tracePreprocState(oldState));
0749: }
0750: CPPParserEx parser = CPPParserEx
0751: .getInstance(
0752: fileBuffer.getFile().getName(),
0753: walker
0754: .getFilteredTokenStream(getLanguageFilter()),
0755: flags);
0756: long time = (emptyAstStatictics) ? System
0757: .currentTimeMillis() : 0;
0758: try {
0759: parser.translation_unit();
0760: } catch (Error ex) {
0761: System.err.println(ex.getClass().getName()
0762: + " at parsing file "
0763: + fileBuffer.getFile().getAbsolutePath()); // NOI18N
0764: throw ex;
0765: }
0766:
0767: if (emptyAstStatictics) {
0768: time = System.currentTimeMillis() - time;
0769: System.err
0770: .println("PARSED FILE "
0771: + getAbsolutePath()
0772: + (AstUtil.isEmpty(parser.getAST(),
0773: true) ? " EMPTY" : "") + ' '
0774: + time + " ms");
0775: }
0776: if (TraceFlags.DUMP_AST) {
0777: System.err.println("\n");
0778: System.err.print("AST: ");
0779: System.err.print(getAbsolutePath());
0780: System.err.print(' ');
0781: AstUtil.toStream(parser.getAST(), System.err);
0782: System.err.println("\n");
0783:
0784: }
0785: errorCount = parser.getErrorCount();
0786: ast = parser.getAST();
0787: // save all in cache
0788: if (state != State.MODIFIED) {
0789: if (TraceFlags.USE_AST_CACHE) {
0790: if (getBuffer().isFileBased()
0791: && !TraceFlags.CACHE_SKIP_SAVE) {
0792: CacheManager.getInstance().saveCache(
0793: this ,
0794: new FileCacheImpl(aptLight, aptFull,
0795: ast));
0796: } else {
0797: if (TraceFlags.TRACE_CACHE) {
0798: System.err
0799: .println("CACHE: not save cache for document based file "
0800: + getAbsolutePath());
0801: }
0802: }
0803: }
0804: } else {
0805: ast = null;
0806: if (TraceFlags.TRACE_CACHE) {
0807: System.err
0808: .println("CACHE: not save cache for file modified during parsing"
0809: + getAbsolutePath());
0810: }
0811: }
0812: }
0813: lastParsed = Math.max(System.currentTimeMillis(), fileBuffer
0814: .lastModified());
0815: if (TraceFlags.TRACE_VALIDATION)
0816: System.err
0817: .printf(
0818: "PARSED %s \n\tlastModified=%d\n\t lastParsed=%d diff=%d\n",
0819: getAbsolutePath(), fileBuffer
0820: .lastModified(), lastParsed,
0821: fileBuffer.lastModified() - lastParsed);
0822: Hook aHook = hook;
0823: if (aHook != null) {
0824: aHook.parsingFinished(this , preprocHandler);
0825: }
0826: return ast;
0827: }
0828:
0829: /*package*/void initGuardIfNeeded(
0830: APTPreprocHandler preprocHandler, APTFile apt) {
0831: if (!getGuardState().isInited()) {
0832: setGuardState(preprocHandler, apt);
0833: }
0834: }
0835:
0836: private void updateGuardAfterParse(
0837: APTPreprocHandler preprocHandler, APTFile apt) {
0838: if (!getGuardState().isInited()) {
0839: setGuardState(preprocHandler, apt);
0840: } else {
0841: getGuardState().setGuardBlockState(preprocHandler,
0842: getGuardState().getGuard());
0843: }
0844: }
0845:
0846: private void setGuardState(APTPreprocHandler preprocHandler,
0847: APTFile aptLight) {
0848: synchronized (getGuardState()) {
0849: GuardBlockWalker guard = new GuardBlockWalker(aptLight,
0850: preprocHandler);
0851: TokenStream ts = guard.getTokenStream();
0852: try {
0853: Token token = ts.nextToken();
0854: while (!APTUtils.isEOF(token)) {
0855: if (!APTUtils.isCommentToken(token)) {
0856: guard.clearGuard();
0857: break;
0858: }
0859: token = ts.nextToken();
0860: }
0861: } catch (TokenStreamException ex) {
0862: guard.clearGuard();
0863: }
0864: getGuardState().setGuardBlockState(preprocHandler,
0865: guard.getGuard());
0866: }
0867: }
0868:
0869: public void addInclude(IncludeImpl includeImpl) {
0870: CsmUID<CsmInclude> inclUID = RepositoryUtils.put(includeImpl);
0871: assert inclUID != null;
0872: try {
0873: includesLock.writeLock().lock();
0874: includes.add(inclUID);
0875: } finally {
0876: includesLock.writeLock().unlock();
0877: }
0878: }
0879:
0880: public static final Comparator<CsmOffsetable> START_OFFSET_COMPARATOR = new Comparator<CsmOffsetable>() {
0881: public int compare(CsmOffsetable o1, CsmOffsetable o2) {
0882: if (o1 == o2) {
0883: return 0;
0884: }
0885: int ofs1 = o1.getStartOffset();
0886: int ofs2 = o2.getStartOffset();
0887: if (ofs1 == ofs2) {
0888: return 0;
0889: } else {
0890: return (ofs1 - ofs2);
0891: }
0892: }
0893:
0894: public @Override
0895: boolean equals(Object obj) {
0896: return super .equals(obj);
0897: }
0898:
0899: public @Override
0900: int hashCode() {
0901: return 11; // any dummy value
0902: }
0903: };
0904:
0905: static final private Comparator<CsmUID> UID_START_OFFSET_COMPARATOR = new Comparator<CsmUID>() {
0906: public int compare(CsmUID o1, CsmUID o2) {
0907: if (o1 == o2) {
0908: return 0;
0909: }
0910: Comparable<CsmUID> i1 = (Comparable<CsmUID>) o1;
0911: assert i1 != null;
0912: return i1.compareTo(o2);
0913: }
0914:
0915: public @Override
0916: boolean equals(Object obj) {
0917: return super .equals(obj);
0918: }
0919:
0920: public @Override
0921: int hashCode() {
0922: return 11; // any dummy value
0923: }
0924: };
0925:
0926: public String getText(int start, int end) {
0927: try {
0928: return fileBuffer.getText(start, end);
0929: } catch (IOException e) {
0930: DiagnosticExceptoins.register(e);
0931: return "";
0932: }
0933: }
0934:
0935: public String getText() {
0936: try {
0937: return fileBuffer.getText();
0938: } catch (IOException e) {
0939: DiagnosticExceptoins.register(e);
0940: return "";
0941: }
0942: }
0943:
0944: public CsmProject getProject() {
0945: return _getProject(true);
0946: }
0947:
0948: /** Just a convenient shortcut to eliminate casts */
0949: public ProjectBase getProjectImpl() {
0950: return _getProject(true);
0951: }
0952:
0953: public CharSequence getName() {
0954: return CharSequenceKey.create(fileBuffer.getFile().getName());
0955: }
0956:
0957: public Collection<CsmInclude> getIncludes() {
0958: Collection<CsmInclude> out;
0959: try {
0960: includesLock.readLock().lock();
0961: out = UIDCsmConverter.UIDsToIncludes(includes);
0962: } finally {
0963: includesLock.readLock().unlock();
0964: }
0965: return out;
0966: }
0967:
0968: public Collection<CsmOffsetableDeclaration> getDeclarations() {
0969: if (!SKIP_UNNECESSARY_FAKE_FIXES) {
0970: fixFakeRegistrations();
0971: }
0972: Collection<CsmOffsetableDeclaration> decls;
0973: try {
0974: declarationsLock.readLock().lock();
0975: Collection<CsmUID<CsmOffsetableDeclaration>> uids = declarations
0976: .values();
0977: decls = UIDCsmConverter.UIDsToDeclarations(uids);
0978: } finally {
0979: declarationsLock.readLock().unlock();
0980: }
0981: return decls;
0982: }
0983:
0984: public void addMacro(CsmMacro macro) {
0985: CsmUID<CsmMacro> macroUID = RepositoryUtils.put(macro);
0986: assert macroUID != null;
0987: try {
0988: macrosLock.writeLock().lock();
0989: macros.add(macroUID);
0990: } finally {
0991: macrosLock.writeLock().unlock();
0992: }
0993: }
0994:
0995: public Collection<CsmMacro> getMacros() {
0996: Collection<CsmMacro> out;
0997: try {
0998: macrosLock.readLock().lock();
0999: out = UIDCsmConverter.UIDsToMacros(macros);
1000: } finally {
1001: macrosLock.readLock().unlock();
1002: }
1003: return out;
1004: }
1005:
1006: public void addDeclaration(CsmOffsetableDeclaration decl) {
1007: CsmUID<CsmOffsetableDeclaration> uidDecl = RepositoryUtils
1008: .put(decl);
1009: try {
1010: declarationsLock.writeLock().lock();
1011: declarations.put(getSortKey(decl), uidDecl);
1012: } finally {
1013: declarationsLock.writeLock().unlock();
1014: }
1015: // TODO: remove this dirty hack!
1016: if (decl instanceof VariableImpl) {
1017: VariableImpl v = (VariableImpl) decl;
1018: if (isOfFileScope(v)) {
1019: v.setScope(this );
1020: }
1021: }
1022: if (CsmKindUtilities.isFunctionDeclaration(decl)) {
1023: FunctionImpl fi = (FunctionImpl) decl;
1024: if (fi.isStatic()) {
1025: addStaticFunctionDeclaration(fi);
1026: }
1027: }
1028: }
1029:
1030: private void addStaticFunctionDeclaration(FunctionImpl func) {
1031: if (staticFunctionDeclarationUIDs == null) {
1032: staticFunctionDeclarationUIDs = new ArrayList<CsmUID<CsmFunction>>();
1033: }
1034: staticFunctionDeclarationUIDs.add(func.getUID());
1035: }
1036:
1037: /**
1038: * Gets the list of the static functions declarations (not definitions)
1039: * This is necessary for finding definitions/declarations
1040: * since file-level static functions (i.e. c-style static functions) aren't registered in project
1041: */
1042: public Collection<CsmFunction> getStaticFunctionDeclarations() {
1043: if (staticFunctionDeclarationUIDs == null) {
1044: return Collections.<CsmFunction> emptyList();
1045: } else {
1046: return new LazyCsmCollection<CsmFunction>(
1047: new ArrayList<CsmUID<CsmFunction>>(
1048: staticFunctionDeclarationUIDs), true);
1049: }
1050: }
1051:
1052: public static boolean isOfFileScope(VariableImpl v) {
1053: if (v.isStatic()) {
1054: return true;
1055: } else if (v.isConst()) {
1056: if (!v.isExtern()) {
1057: return true;
1058: }
1059: } else {
1060: return false;
1061: // if( ! v.isExtern() ) {
1062: // return true;
1063: // }
1064: }
1065: return false;
1066: }
1067:
1068: public void removeDeclaration(CsmOffsetableDeclaration declaration) {
1069: _removeDeclaration(declaration);
1070: }
1071:
1072: private void _removeDeclaration(CsmOffsetableDeclaration declaration) {
1073: CsmUID<CsmOffsetableDeclaration> uidDecl;
1074: try {
1075: declarationsLock.writeLock().lock();
1076: uidDecl = declarations.remove(getSortKey(declaration));
1077: } finally {
1078: declarationsLock.writeLock().unlock();
1079: }
1080: RepositoryUtils.remove(uidDecl);
1081: // update repository
1082: RepositoryUtils.put(this );
1083: }
1084:
1085: private SortedKey getSortKey(CsmOffsetableDeclaration declaration) {
1086: return new SortedKey(declaration);
1087: }
1088:
1089: public String getAbsolutePath() {
1090: return fileBuffer.getFile().getAbsolutePath();
1091: }
1092:
1093: public File getFile() {
1094: return fileBuffer.getFile();
1095: }
1096:
1097: public Collection<CsmScopeElement> getScopeElements() {
1098: List<CsmScopeElement> l = new ArrayList<CsmScopeElement>();
1099: //TODO: add static functions
1100: for (Iterator iter = getDeclarations().iterator(); iter
1101: .hasNext();) {
1102: CsmDeclaration decl = (CsmDeclaration) iter.next();
1103: // TODO: remove this dirty hack!
1104: if (decl instanceof VariableImpl) {
1105: VariableImpl v = (VariableImpl) decl;
1106: if (isOfFileScope(v)) {
1107: l.add(v);
1108: }
1109: }
1110: }
1111: return l;
1112: }
1113:
1114: public boolean isValid() {
1115: CsmProject project = _getProject(false);
1116: return project != null && project.isValid();
1117: }
1118:
1119: public boolean isParsed() {
1120: synchronized (changeStateLock) {
1121: return state == State.PARSED;
1122: }
1123: }
1124:
1125: public boolean isParsingOrParsed() {
1126: synchronized (changeStateLock) {
1127: return state == State.PARSED || state == State.BEING_PARSED;
1128: }
1129: }
1130:
1131: public void scheduleParsing(boolean wait)
1132: throws InterruptedException {
1133: scheduleParsing(wait, null);
1134: }
1135:
1136: public void scheduleParsing(boolean wait,
1137: APTPreprocHandler.State ppState)
1138: throws InterruptedException {
1139: //if( TraceFlags.TRACE_PARSER_QUEUE ) System.err.println("> File " + getName() + " @" + hashCode() + " waiting for parse; thread: " + Thread.currentThread().getName());
1140: boolean fixFakes = false;
1141: synchronized (stateLock) {
1142: //if( TraceFlags.TRACE_PARSER_QUEUE ) System.err.println(" sync " + getName() + " @" + hashCode() + " waiting for parse; thread: " + Thread.currentThread().getName());
1143: if (SKIP_UNNECESSARY_FAKE_FIXES) {
1144: if (isParsed()) {
1145: fixFakes = wait;
1146: } else {
1147: while (!isParsed()) {
1148: ParserQueue.instance().addFirst(this , ppState,
1149: false);
1150: //if( TraceFlags.TRACE_PARSER_QUEUE ) System.err.println(" !prs " + getName() + " @" + hashCode() + " waiting for parse; thread: " + Thread.currentThread().getName());
1151: if (wait) {
1152: stateLock.wait();
1153: } else {
1154: return;
1155: }
1156: //if( TraceFlags.TRACE_PARSER_QUEUE ) System.err.println("< wait " + getName() + " @" + hashCode() + " waiting for parse; thread: " + Thread.currentThread().getName());
1157: }
1158: }
1159: } else {
1160: while (!isParsed()) {
1161: ParserQueue.instance().addFirst(this , ppState,
1162: false);
1163: //if( TraceFlags.TRACE_PARSER_QUEUE ) System.err.println(" !prs " + getName() + " @" + hashCode() + " waiting for parse; thread: " + Thread.currentThread().getName());
1164: if (wait) {
1165: stateLock.wait();
1166: } else {
1167: return;
1168: }
1169: //if( TraceFlags.TRACE_PARSER_QUEUE ) System.err.println("< wait " + getName() + " @" + hashCode() + " waiting for parse; thread: " + Thread.currentThread().getName());
1170: }
1171: }
1172: }
1173: if (SKIP_UNNECESSARY_FAKE_FIXES && fixFakes) {
1174: fixFakeRegistrations();
1175: }
1176: //if( TraceFlags.TRACE_PARSER_QUEUE ) System.err.println("< File " + getName() + " @" + hashCode() + " waiting for parse; thread: " + Thread.currentThread().getName());
1177: }
1178:
1179: public void onFakeRegisration(FunctionImplEx decl) {
1180: CsmUID<FunctionImplEx> uidDecl = UIDCsmConverter
1181: .declarationToUID(decl);
1182: fakeRegistrationUIDs.add(uidDecl);
1183: }
1184:
1185: public void fixFakeRegistrations() {
1186: if (!isValid()) {
1187: return;
1188: }
1189: Collection<FunctionImplEx> fakes = Collections
1190: .<FunctionImplEx> emptySet();
1191:
1192: if (fakeRegistrationUIDs.size() > 0) {
1193: fakes = UIDCsmConverter
1194: .UIDsToDeclarationsUnsafe(fakeRegistrationUIDs);
1195: fakeRegistrationUIDs.clear();
1196: }
1197: for (FunctionImplEx curElem : fakes) {
1198: // Due to lazy list the client should check value on null.
1199: if (curElem != null) {
1200: curElem.fixFakeRegistration();
1201: }
1202: }
1203: }
1204:
1205: public @Override
1206: String toString() {
1207: return "FileImpl @" + hashCode() + ' ' + getAbsolutePath(); // NOI18N
1208: }
1209:
1210: public CsmUID<CsmFile> getUID() {
1211: if (uid == null) {
1212: uid = UIDUtilities.createFileUID(this );
1213: }
1214: return uid;
1215: }
1216:
1217: private CsmUID<CsmFile> uid = null;
1218:
1219: ////////////////////////////////////////////////////////////////////////////
1220: // impl of persistent
1221:
1222: public void write(DataOutput output) throws IOException {
1223: PersistentUtils.writeBuffer(this .fileBuffer, output);
1224: UIDObjectFactory factory = UIDObjectFactory.getDefaultFactory();
1225: try {
1226: declarationsLock.readLock().lock();
1227: factory.writeSortedStringToUIDMap(this .declarations,
1228: output, false);
1229: } finally {
1230: declarationsLock.readLock().unlock();
1231: }
1232: try {
1233: includesLock.readLock().lock();
1234: factory.writeUIDCollection(this .includes, output, false);
1235: } finally {
1236: includesLock.readLock().unlock();
1237: }
1238: try {
1239: macrosLock.readLock().lock();
1240: factory.writeUIDCollection(this .macros, output, false);
1241: } finally {
1242: macrosLock.readLock().unlock();
1243: }
1244: factory.writeUIDCollection(this .fakeRegistrationUIDs, output,
1245: false);
1246: //output.writeUTF(state.toString());
1247: output.writeInt(fileType);
1248:
1249: // not null UID
1250: assert this .projectUID != null;
1251: UIDObjectFactory.getDefaultFactory().writeUID(this .projectUID,
1252: output);
1253: guardState.write(output);
1254: output.writeLong(lastParsed);
1255: output.writeUTF(state.toString());
1256: }
1257:
1258: public FileImpl(DataInput input) throws IOException {
1259: this .fileBuffer = PersistentUtils.readBuffer(input);
1260:
1261: UIDObjectFactory factory = UIDObjectFactory.getDefaultFactory();
1262: factory
1263: .readSortedStringToUIDMap(this .declarations, input,
1264: null);
1265: factory.readUIDCollection(this .includes, input);
1266: factory.readUIDCollection(this .macros, input);
1267: factory.readUIDCollection(this .fakeRegistrationUIDs, input);
1268: //state = State.valueOf(input.readUTF());
1269: fileType = input.readInt();
1270:
1271: this .projectUID = UIDObjectFactory.getDefaultFactory().readUID(
1272: input);
1273: // not null UID
1274: assert this .projectUID != null;
1275: this .projectRef = null;
1276:
1277: assert fileBuffer != null;
1278: assert fileBuffer.isFileBased();
1279: guardState = new GuardBlockState(input);
1280: lastParsed = input.readLong();
1281: state = State.valueOf(input.readUTF());
1282: }
1283:
1284: public @Override
1285: int hashCode() {
1286: if (hash == 0) { // we don't need sync here - at worst, we'll calculate the same value twice
1287: String identityHashPath = getProjectImpl().getUniqueName()
1288: + "*" + getAbsolutePath(); // NOI18N
1289: hash = identityHashPath.hashCode();
1290: }
1291: return hash;
1292: }
1293:
1294: public @Override
1295: boolean equals(Object obj) {
1296: if (obj == null || !(obj instanceof FileImpl)) {
1297: return false;
1298: }
1299: if (obj == this ) {
1300: return true;
1301: }
1302: FileImpl other = (FileImpl) obj;
1303: if (this .getAbsolutePath().equals(other.getAbsolutePath())) {
1304: return this .getProjectImpl().getUniqueName().equals(
1305: other.getProjectImpl().getUniqueName());
1306: }
1307: return false;
1308: }
1309:
1310: private GuardBlockState getGuardState() {
1311: return guardState;
1312: }
1313:
1314: // for tests only
1315: public GuardBlockState testGetGuardState() {
1316: return guardState;
1317: }
1318:
1319: public boolean isNeedReparse(APTPreprocHandler.State oldState,
1320: APTPreprocHandler newState) {
1321: boolean update = false;
1322: if (oldState == null || !oldState.isValid()) {
1323: update = true;
1324: } else if (!oldState.isCompileContext()
1325: && newState.isCompileContext()) {
1326: update = true;
1327: } else {
1328: update = getGuardState().isNeedReparse(newState);
1329: }
1330: return update;
1331: }
1332:
1333: public boolean isNeedReparse(APTPreprocHandler.State oldState,
1334: APTPreprocHandler.State preprocState) {
1335: if (oldState == null || !oldState.isValid()) {
1336: return true;
1337: } else if (oldState.isCompileContext()) {
1338: // do nothing
1339: if (preprocState != null
1340: && isNeedReparseGuardBlock(preprocState)) {
1341: // override state with new one
1342: return true;
1343: }
1344: } else if (preprocState != null
1345: && preprocState.isCompileContext()) {
1346: // override state with new one
1347: return true;
1348: }
1349: return false;
1350: }
1351:
1352: private boolean isNeedReparseGuardBlock(
1353: APTPreprocHandler.State preprocState) {
1354: StartEntry startEntry = new StartEntry(getAbsolutePath(),
1355: RepositoryUtils.UIDtoKey(getProject().getUID()));
1356: APTPreprocHandler preprocHandler = APTHandlersSupport
1357: .createEmptyPreprocHandler(startEntry);
1358: preprocHandler.setState(preprocState);
1359: return getGuardState().isNeedReparse(preprocHandler);
1360: }
1361:
1362: public int getOffset(int line, int column) {
1363: if (line <= 0 || column <= 0) {
1364: throw new IllegalArgumentException(
1365: "line and column are 1-based"); // NOI18N
1366: }
1367: int offset = 0;
1368: int curLine = 1;
1369: String text = getText();
1370: // find line
1371: for (; offset < text.length() && curLine < line; offset++) {
1372: if (text.charAt(offset) == '\n') {
1373: curLine++;
1374: }
1375: }
1376: // check line
1377: if (curLine < line) {
1378: throw new IllegalStateException("no line with index "
1379: + line + " in file " + getAbsolutePath()); // NOI18N
1380: }
1381: int outOffset = offset + (column - 1);
1382: // check that column is valid: not on the next line
1383: if (text.length() < outOffset
1384: || (text.substring(offset, outOffset).indexOf('\n') >= 0)) { // NOI18N
1385: throw new IllegalStateException("no column with index "
1386: + column + " in file " + getAbsolutePath()); // NOI18N
1387: }
1388: return outOffset;
1389: }
1390:
1391: /**
1392: * returns 1-based line and column associated with offset
1393: * @param offset interested offset in file
1394: * @return returns pair {line, column}
1395: */
1396: public int[] getLineColumn(int offset) {
1397: int[] lineCol = new int[] { 1, 1 };
1398: String text = getText();
1399: if (text.length() < offset) {
1400: throw new IllegalArgumentException(
1401: "offset is out of file length; "
1402: + // NOI18N
1403: (getBuffer().isFileBased() ? "file based"
1404: : "document based")
1405: + // NOI18N
1406: " file=" + this .getAbsolutePath()
1407: + // NOI18N
1408: ";length=" + text.length() + "; offset="
1409: + offset); // NOI18N
1410: }
1411: final int TABSIZE = ModelSupport.getTabSize();
1412: // find line and column
1413: for (int curOffset = 0; curOffset < offset; curOffset++) {
1414: char curChar = text.charAt(curOffset);
1415: if (curChar == '\n') {
1416: // just increase line number
1417: lineCol[0] = lineCol[0] + 1;
1418: lineCol[1] = 1;
1419: } else if (curChar == '\t') {
1420: int col = lineCol[1];
1421: int newCol = (((col - 1) / TABSIZE) + 1) * TABSIZE + 1;
1422: lineCol[1] = newCol;
1423: } else {
1424: lineCol[1]++;
1425: }
1426: }
1427: return lineCol;
1428: }
1429:
1430: public static class SortedKey implements Comparable<SortedKey>,
1431: Persistent, SelfPersistent {
1432: private int start = 0;
1433: private CharSequence name;
1434:
1435: private SortedKey(CsmOffsetableDeclaration declaration) {
1436: start = ((CsmOffsetable) declaration).getStartOffset();
1437: name = declaration.getName();
1438: }
1439:
1440: public int compareTo(SortedKey o) {
1441: int res = start - o.start;
1442: if (res == 0) {
1443: res = CharSequenceKey.Comparator.compare(name, o.name);
1444: }
1445: return res;
1446: }
1447:
1448: public void write(DataOutput output) throws IOException {
1449: output.writeInt(start);
1450: output.writeUTF(name.toString());
1451: }
1452:
1453: public SortedKey(DataInput input) throws IOException {
1454: start = input.readInt();
1455: name = NameCache.getManager().getString(input.readUTF());
1456: }
1457: }
1458:
1459: }
|