PageRenderTime 60ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/netbeans-7.3/cnd.modelimpl/src/org/netbeans/modules/cnd/modelimpl/content/project/FileContainer.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1037 lines | 844 code | 102 blank | 91 comment | 270 complexity | c87e6c060fe75e73e9f2aef7fce5003e MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans.modules.cnd.modelimpl.content.project;
  45. import java.io.IOException;
  46. import java.util.ArrayList;
  47. import java.util.Arrays;
  48. import java.util.Collection;
  49. import java.util.Collections;
  50. import java.util.Iterator;
  51. import java.util.List;
  52. import java.util.Map;
  53. import java.util.Set;
  54. import java.util.TreeMap;
  55. import java.util.concurrent.ConcurrentHashMap;
  56. import java.util.concurrent.ConcurrentMap;
  57. import java.util.logging.Level;
  58. import org.netbeans.modules.cnd.api.model.CsmFile;
  59. import org.netbeans.modules.cnd.api.model.CsmUID;
  60. import org.netbeans.modules.cnd.debug.DebugUtils;
  61. import org.netbeans.modules.cnd.apt.support.APTPreprocHandler;
  62. import org.netbeans.modules.cnd.apt.support.APTHandlersSupport;
  63. import org.netbeans.modules.cnd.apt.support.APTPreprocHandler.State;
  64. import org.netbeans.modules.cnd.apt.utils.APTSerializeUtils;
  65. import org.netbeans.modules.cnd.modelimpl.csm.core.FileImpl;
  66. import org.netbeans.modules.cnd.modelimpl.csm.core.FilePreprocessorConditionState;
  67. import org.netbeans.modules.cnd.modelimpl.csm.core.PreprocessorStatePair;
  68. import org.netbeans.modules.cnd.modelimpl.csm.core.ProjectBase;
  69. import org.netbeans.modules.cnd.modelimpl.csm.core.Utils;
  70. import org.netbeans.modules.cnd.modelimpl.debug.DiagnosticExceptoins;
  71. import org.netbeans.modules.cnd.utils.cache.APTStringManager;
  72. import org.netbeans.modules.cnd.utils.cache.FilePathCache;
  73. import org.netbeans.modules.cnd.modelimpl.debug.TraceFlags;
  74. import org.netbeans.modules.cnd.modelimpl.repository.FileContainerKey;
  75. import org.netbeans.modules.cnd.modelimpl.repository.KeyUtilities;
  76. import org.netbeans.modules.cnd.modelimpl.repository.PersistentUtils;
  77. import org.netbeans.modules.cnd.modelimpl.repository.RepositoryUtils;
  78. import org.netbeans.modules.cnd.modelimpl.textcache.DefaultCache;
  79. import org.netbeans.modules.cnd.modelimpl.uid.KeyBasedUID;
  80. import org.netbeans.modules.cnd.modelimpl.uid.LazyCsmCollection;
  81. import org.netbeans.modules.cnd.modelimpl.uid.UIDCsmConverter;
  82. import org.netbeans.modules.cnd.modelimpl.uid.UIDObjectFactory;
  83. import org.netbeans.modules.cnd.modelimpl.uid.UIDUtilities;
  84. import org.netbeans.modules.cnd.repository.spi.Persistent;
  85. import org.netbeans.modules.cnd.repository.spi.RepositoryDataInput;
  86. import org.netbeans.modules.cnd.repository.spi.RepositoryDataOutput;
  87. import org.netbeans.modules.cnd.repository.support.SelfPersistent;
  88. import org.netbeans.modules.cnd.spi.utils.CndFileSystemProvider;
  89. import org.netbeans.modules.cnd.utils.CndPathUtilitities;
  90. import org.netbeans.modules.cnd.utils.CndUtils;
  91. import org.netbeans.modules.cnd.utils.cache.CndFileUtils;
  92. import org.openide.filesystems.FileObject;
  93. import org.openide.filesystems.FileSystem;
  94. /**
  95. * Storage for files and states. Class was extracted from ProjectBase.
  96. * @author Alexander Simon
  97. */
  98. public class FileContainer extends ProjectComponent implements Persistent, SelfPersistent {
  99. private static final boolean TRACE_PP_STATE_OUT = DebugUtils.getBoolean("cnd.dump.preproc.state", false);
  100. private static final class Lock {}
  101. private final Object lock = new Lock();
  102. private final Map<CharSequence, FileEntry> myFiles = new ConcurrentHashMap<CharSequence, FileEntry>();
  103. private final ConcurrentMap<CharSequence, Object/*CharSequence or CharSequence[]*/> canonicFiles = new ConcurrentHashMap<CharSequence, Object/*CharSequence or CharSequence[]*/>();
  104. private final FileSystem fileSystem;
  105. // empty stub
  106. private static final FileContainer EMPTY = new FileContainer() {
  107. @Override
  108. public void put() {
  109. // do nothing
  110. }
  111. @Override
  112. public void putFile(FileImpl impl, State state) {
  113. // do nothing
  114. }
  115. };
  116. /** Creates a new instance of FileContainer */
  117. public FileContainer(ProjectBase project) {
  118. super(new FileContainerKey(project.getUnitId()));
  119. fileSystem = project.getFileSystem();
  120. put();
  121. }
  122. public FileContainer (RepositoryDataInput input) throws IOException {
  123. super(input);
  124. fileSystem = PersistentUtils.readFileSystem(input);
  125. readStringToFileEntryMap(fileSystem, getUnitId(), input, myFiles);
  126. readStringToStringsArrMap(getUnitId(), input, canonicFiles);
  127. //trace(canonicFiles, "Read in ctor:");
  128. if (CndUtils.isDebugMode()) {
  129. checkConsistency();
  130. }
  131. }
  132. // only for creating EMPTY stub
  133. private FileContainer() {
  134. super((org.netbeans.modules.cnd.repository.spi.Key) null);
  135. fileSystem = null;
  136. }
  137. public static FileContainer empty() {
  138. return EMPTY;
  139. }
  140. private void trace(Map<CharSequence, Object/*String or CharSequence[]*/> map, String title) {
  141. System.err.printf("%s\n", title);
  142. for( Map.Entry<CharSequence, Object> entry : map.entrySet() ) {
  143. System.err.printf("%s ->\n%s\n\n", entry.getKey(), entry.getValue());
  144. }
  145. }
  146. public void putFile(FileImpl impl, APTPreprocHandler.State state) {
  147. if (CndUtils.isDebugMode()) {
  148. checkConsistency();
  149. }
  150. CharSequence path = getFileKey(impl.getAbsolutePath(), true);
  151. CharSequence canonicalPath = getCanonicalKey(path);
  152. FileEntry newEntry;
  153. CsmUID<CsmFile> uid = RepositoryUtils.<CsmFile>put(impl);
  154. newEntry = new FileEntry(uid, state, path, canonicalPath);
  155. FileEntry old;
  156. old = myFiles.put(path, newEntry);
  157. addAlternativeFileKey(path, newEntry.canonical);
  158. if (old != null){
  159. System.err.println("Replace file info for "+ old.fileNew + " with " + impl);
  160. }
  161. if (CndUtils.isDebugMode()) {
  162. checkConsistency();
  163. }
  164. }
  165. public void removeFile(CharSequence file) {
  166. CharSequence path = getFileKey(file, false);
  167. FileEntry f;
  168. if (CndUtils.isDebugMode()) {
  169. checkConsistency();
  170. }
  171. f = myFiles.remove(path);
  172. if (f != null) {
  173. removeAlternativeFileKey(f.canonical, path);
  174. }
  175. if (f != null) {
  176. if (f.fileNew != null){
  177. // clean repository
  178. if (false) { RepositoryUtils.remove(f.fileNew, null) ;}
  179. }
  180. }
  181. if (CndUtils.isDebugMode()) {
  182. checkConsistency();
  183. }
  184. }
  185. public FileImpl getFile(CharSequence absPath, boolean treatSymlinkAsSeparateFile) {
  186. FileEntry f = getFileEntry(absPath, treatSymlinkAsSeparateFile, false);
  187. return getFile(f);
  188. }
  189. private FileImpl getFile(FileEntry f) {
  190. if (f == null) {
  191. return null;
  192. }
  193. CsmUID<CsmFile> fileUID = f.fileNew;
  194. FileImpl impl = (FileImpl) UIDCsmConverter.UIDtoFile(f.fileNew);
  195. if( impl == null ) {
  196. String postfix = ""; // NOI18N
  197. if (CndUtils.isDebugMode() || CndUtils.isUnitTestMode()) {
  198. FileImpl impl2 = (FileImpl) UIDCsmConverter.UIDtoFile(f.fileNew);
  199. if (impl2 == null) {
  200. postfix = " TWICE"; // NOI18N
  201. } else {
  202. postfix = " second attempt OK"; // NOI18N
  203. }
  204. }
  205. if (fileUID instanceof KeyBasedUID) {
  206. DiagnosticExceptoins.registerIllegalRepositoryStateException("no file for UID " + postfix, fileUID); // NOI18N
  207. }
  208. }
  209. return impl;
  210. }
  211. public CsmUID<CsmFile> getFileUID(CharSequence absPath, boolean treatSymlinkAsSeparateFile) {
  212. FileEntry f = getFileEntry(absPath, treatSymlinkAsSeparateFile, false);
  213. if (f == null) {
  214. return null;
  215. }
  216. return f.fileNew;
  217. }
  218. public void invalidatePreprocState(CharSequence absPath) {
  219. FileEntry f = getFileEntry(absPath, false, false);
  220. if (f == null){
  221. return;
  222. }
  223. synchronized (f) {
  224. f.invalidateStates();
  225. }
  226. if (TRACE_PP_STATE_OUT) {
  227. CharSequence path = getFileKey(absPath, false);
  228. System.err.println("\nInvalidated state for file" + path + "\n");
  229. }
  230. }
  231. public void markAsParsingPreprocStates(CharSequence absPath) {
  232. FileEntry f = getFileEntry(absPath, false, false);
  233. if (f == null) {
  234. return;
  235. }
  236. synchronized (f) {
  237. f.markAsParsingPreprocStates();
  238. }
  239. if (TRACE_PP_STATE_OUT) {
  240. CharSequence path = getFileKey(absPath, false);
  241. System.err.println("\nmarkAsParsingPreprocStates for file" + path + "\n");
  242. }
  243. }
  244. public FileEntry getEntry(CharSequence absPath) {
  245. CndUtils.assertTrue(CndPathUtilitities.isPathAbsolute(absPath), "Path should be absolute: ", absPath); //NOI18N
  246. return getFileEntry(absPath, false, false);
  247. }
  248. public Object getLock(CharSequence absPath) {
  249. FileEntry f = getFileEntry(absPath, false, false);
  250. return f == null ? lock : f.getLock();
  251. }
  252. public void debugClearState(){
  253. List<FileEntry> files;
  254. files = new ArrayList<FileEntry>(myFiles.values());
  255. for (FileEntry file : files){
  256. synchronized (file.getLock()) {
  257. file.debugClearState();
  258. }
  259. }
  260. put();
  261. }
  262. public Collection<CsmFile> getFiles() {
  263. List<CsmUID<CsmFile>> uids = new ArrayList<CsmUID<CsmFile>>(myFiles.values().size());
  264. getFiles2(uids);
  265. return new LazyCsmCollection<CsmFile, CsmFile>(uids, TraceFlags.SAFE_UID_ACCESS);
  266. }
  267. public Collection<CsmUID<CsmFile>> getFilesUID() {
  268. List<CsmUID<CsmFile>> uids = new ArrayList<CsmUID<CsmFile>>(myFiles.values().size());
  269. getFiles2(uids);
  270. return uids;
  271. }
  272. public Collection<FileImpl> getFileImpls() {
  273. List<CsmUID<CsmFile>> uids = new ArrayList<CsmUID<CsmFile>>(myFiles.values().size());
  274. getFiles2(uids);
  275. return new LazyCsmCollection<CsmFile, FileImpl>(uids, TraceFlags.SAFE_UID_ACCESS);
  276. }
  277. private void getFiles2(List<CsmUID<CsmFile>> res) {
  278. List<FileEntry> files;
  279. files = new ArrayList<FileEntry>(myFiles.values());
  280. for(FileEntry f : files){
  281. res.add(f.fileNew);
  282. }
  283. }
  284. public int getSize(){
  285. return myFiles.size();
  286. }
  287. @Override
  288. public void write(RepositoryDataOutput aStream) throws IOException {
  289. super.write(aStream);
  290. PersistentUtils.writeFileSystem(fileSystem, aStream);
  291. // maps are concurrent, so we don't need synchronization here
  292. writeStringToFileEntryMap(getUnitId(), aStream, myFiles);
  293. writeStringToStringsArrMap(getUnitId(), aStream, canonicFiles);
  294. //trace(canonicFiles, "Wrote in write()");
  295. }
  296. public static CharSequence getFileKey(CharSequence file, boolean sharedText) {
  297. return sharedText ? FilePathCache.getManager().getString(file) : DefaultCache.getManager().getString(file);
  298. }
  299. private CharSequence getAlternativeFileKey(CharSequence primaryKey) {
  300. Object out = canonicFiles.get(primaryKey);
  301. if (out instanceof CharSequence) {
  302. return (CharSequence)out;
  303. } else if (out != null) {
  304. assert ((CharSequence[])out).length >= 2;
  305. return ((CharSequence[])out)[0];
  306. }
  307. return null;
  308. }
  309. private FileEntry getFileEntry(CharSequence absPath, boolean treatSymlinkAsSeparateFile, boolean sharedText) {
  310. return getFileEntryImpl(getFileKey(absPath, sharedText), treatSymlinkAsSeparateFile);
  311. }
  312. /**
  313. * NB: path should be got via getFileKey!
  314. * to be called only from within getFileEntry
  315. */
  316. private FileEntry getFileEntryImpl(CharSequence path, boolean treatSymlinkAsSeparateFile) {
  317. FileEntry f = myFiles.get(path);
  318. if (f == null && (!treatSymlinkAsSeparateFile || !TraceFlags.SYMLINK_AS_OWN_FILE)) {
  319. // check alternative expecting that 'path' is canonical path
  320. CharSequence path2 = getAlternativeFileKey(path);
  321. f = (path2 == null) ? null : myFiles.get(path2);
  322. if (f != null) {
  323. if (TraceFlags.TRACE_CANONICAL_FIND_FILE) {
  324. CndUtils.assertTrueInConsole(false, "alternative for " + path + " is " + path2); // NOI18N
  325. }
  326. }
  327. }
  328. return f;
  329. }
  330. private void addAlternativeFileKey(CharSequence primaryKey, CharSequence canonicKey) {
  331. Object out = canonicFiles.get(canonicKey);
  332. Object newVal;
  333. if (out == null) {
  334. newVal = primaryKey;
  335. } else {
  336. if (out instanceof CharSequence) {
  337. if (out.equals(primaryKey)) {
  338. return;
  339. }
  340. newVal = new CharSequence[] {(CharSequence)out, primaryKey};
  341. } else {
  342. CharSequence[] oldAr = (CharSequence[])out;
  343. for(CharSequence what:oldAr){
  344. if (what.equals(primaryKey)){
  345. return;
  346. }
  347. }
  348. CharSequence[] newAr = new CharSequence[oldAr.length + 1];
  349. System.arraycopy(oldAr, 0, newAr, 0, oldAr.length);
  350. newAr[oldAr.length] = primaryKey;
  351. newVal = newAr;
  352. }
  353. }
  354. canonicFiles.put(canonicKey, newVal);
  355. if (TraceFlags.TRACE_CANONICAL_FIND_FILE) {
  356. if (newVal instanceof CharSequence[]) {
  357. System.err.println("entry for " + canonicKey + " while adding " + primaryKey + " is " + Arrays.asList((CharSequence[])newVal).toString());
  358. } else {
  359. System.err.println("entry for " + canonicKey + " while adding " + primaryKey + " is " + newVal);
  360. }
  361. }
  362. }
  363. private void removeAlternativeFileKey(CharSequence canonicKey, CharSequence primaryKey) {
  364. Object out = canonicFiles.get(canonicKey);
  365. assert out != null : "no entry for " + canonicKey + " of " + primaryKey;
  366. Object newVal;
  367. if (out instanceof CharSequence) {
  368. assert primaryKey.equals(out) : " primaryKey " + primaryKey + " have to be " + out;
  369. newVal = null;
  370. } else {
  371. CharSequence[] oldAr = (CharSequence[])out;
  372. assert oldAr.length >= 2;
  373. if (oldAr.length == 2) {
  374. assert oldAr[0].equals(primaryKey) || oldAr[1].equals(primaryKey) : "no primaryKey " + primaryKey + " in " + Arrays.toString(oldAr);
  375. newVal = oldAr[0].equals(primaryKey) ? oldAr[1] : oldAr[0];
  376. } else {
  377. CharSequence[] newAr = new CharSequence[oldAr.length - 1];
  378. int k = 0;
  379. boolean found = false;
  380. for(CharSequence cur : oldAr){
  381. if (!cur.equals(primaryKey)){
  382. newAr[k++]=cur;
  383. } else {
  384. found = true;
  385. }
  386. }
  387. assert found : " not found " + primaryKey + " in " + oldAr;
  388. newVal = newAr;
  389. }
  390. }
  391. if (newVal == null) {
  392. boolean removed = canonicFiles.remove(canonicKey, out);
  393. CndUtils.assertTrue(removed, "inconsistent state for ", primaryKey);
  394. if (TraceFlags.TRACE_CANONICAL_FIND_FILE) {
  395. System.err.println("removed entry for " + canonicKey + " while removing " + primaryKey);
  396. }
  397. } else {
  398. Object prevValue = canonicFiles.put(canonicKey, newVal);
  399. CndUtils.assertTrue(prevValue == out, "inconsistent state for ", primaryKey);
  400. if (TraceFlags.TRACE_CANONICAL_FIND_FILE) {
  401. System.err.println("change entry for " + canonicKey + " while removing " + primaryKey + " to " + newVal);
  402. }
  403. }
  404. }
  405. private void checkConsistency() {
  406. int valuesSize = 0;
  407. for (Map.Entry<CharSequence, Object> entry : this.canonicFiles.entrySet()) {
  408. Object value = entry.getValue();
  409. assert value != null;
  410. CharSequence[] absPaths;
  411. if (value instanceof CharSequence) {
  412. absPaths = new CharSequence[] { (CharSequence)value };
  413. } else {
  414. absPaths = (CharSequence[]) value;
  415. }
  416. valuesSize += absPaths.length;
  417. for (CharSequence path : absPaths) {
  418. if (!myFiles.containsKey(path)) {
  419. CndUtils.assertTrueInConsole(false, "no Entry for registered absPath " + path + " with canonical " + entry.getKey());
  420. }
  421. }
  422. }
  423. assert valuesSize == myFiles.size() : "different number of elements " + myFiles.size() + " vs " + valuesSize;
  424. }
  425. /*package*/ static void writeStringToFileEntryMap (
  426. int unitIndex, final RepositoryDataOutput output, Map<CharSequence, FileEntry> aMap) throws IOException {
  427. assert output != null;
  428. assert aMap != null;
  429. int size = aMap.size();
  430. //write size
  431. output.writeInt(size);
  432. // write the map
  433. final Set<Map.Entry<CharSequence, FileEntry>> entrySet = aMap.entrySet();
  434. final Iterator <Map.Entry<CharSequence, FileEntry>> setIterator = entrySet.iterator();
  435. while (setIterator.hasNext()) {
  436. final Map.Entry<CharSequence, FileEntry> anEntry = setIterator.next();
  437. PersistentUtils.writeFileNameIndex(anEntry.getKey(), output, unitIndex);
  438. // TODO: replace call above by the next line if no failures in following asserts
  439. // it allows to eleminate charSeq->fileIndex conversion when fileIndex is
  440. // known from fileNew UID kept in value's FileEntry
  441. // output.writeInt(UIDUtilities.getFileID(anEntry.getValue().fileNew));
  442. assert anEntry.getValue() != null;
  443. // see IncludedFileContainer.getIncludedUnitId
  444. if (false && (CndUtils.isDebugMode() || CndUtils.isUnitTestMode())) {
  445. int projectIDFromFileEntry = UIDUtilities.getProjectID(anEntry.getValue().fileNew);
  446. CndUtils.assertTrueInConsole(projectIDFromFileEntry == unitIndex, anEntry.getValue().fileNew + "is not from unit " + unitIndex + " but from " + projectIDFromFileEntry);
  447. int fileIDFromFileEntry = UIDUtilities.getFileID(anEntry.getValue().fileNew);
  448. int fileIdByNameFromUnitId = KeyUtilities.getFileIdByName(unitIndex, anEntry.getKey());
  449. CndUtils.assertTrueInConsole(fileIDFromFileEntry == fileIdByNameFromUnitId, fileIDFromFileEntry + ":" + projectIDFromFileEntry + " differs from " + fileIdByNameFromUnitId + ":" + unitIndex);
  450. }
  451. anEntry.getValue().write(output, unitIndex);
  452. }
  453. }
  454. /*package*/ static void readStringToFileEntryMap(
  455. FileSystem fs, int unitIndex,
  456. RepositoryDataInput input, Map<CharSequence, FileEntry> aMap) throws IOException {
  457. assert input != null;
  458. assert aMap != null;
  459. final APTStringManager pathManager = FilePathCache.getManager();
  460. aMap.clear();
  461. final int size = input.readInt();
  462. for (int i = 0; i < size; i++) {
  463. CharSequence key = PersistentUtils.readFileNameIndex(input, pathManager, unitIndex);
  464. FileEntry value = new FileEntry(fs, input, unitIndex);
  465. assert key != null;
  466. assert value != null;
  467. aMap.put(key, value);
  468. }
  469. }
  470. private static void writeStringToStringsArrMap (
  471. final int unitIndex, final RepositoryDataOutput output,
  472. final Map<CharSequence, Object/*CharSequence or CharSequence[]*/> aMap) throws IOException {
  473. assert output != null;
  474. assert aMap != null;
  475. final int size = aMap.size();
  476. output.writeInt(size);
  477. final Set<Map.Entry<CharSequence, Object>> entrySet = aMap.entrySet();
  478. final Iterator<Map.Entry<CharSequence, Object>> setIterator = entrySet.iterator();
  479. while (setIterator.hasNext()) {
  480. final Map.Entry<CharSequence, Object> anEntry = setIterator.next();
  481. assert anEntry != null;
  482. final CharSequence key = anEntry.getKey();
  483. final Object value = anEntry.getValue();
  484. assert key != null;
  485. assert value != null;
  486. assert ((value instanceof CharSequence) || (value instanceof CharSequence[]));
  487. APTSerializeUtils.writeFileNameIndex(key, output, unitIndex);
  488. if (value instanceof CharSequence ) {
  489. output.writeInt(1);
  490. APTSerializeUtils.writeFileNameIndex((CharSequence)value, output, unitIndex);
  491. } else if (value instanceof CharSequence[]) {
  492. final CharSequence[] array = (CharSequence[]) value;
  493. output.writeInt(array.length);
  494. for (int j = 0; j < array.length; j++) {
  495. APTSerializeUtils.writeFileNameIndex(array[j], output, unitIndex);
  496. }
  497. }
  498. }
  499. }
  500. private static void readStringToStringsArrMap(
  501. final int unitId, final RepositoryDataInput input, Map<CharSequence,
  502. Object/*CharSequence or CharSequence[]*/> aMap) throws IOException {
  503. assert input != null;
  504. assert aMap != null;
  505. final APTStringManager pathManager = FilePathCache.getManager();
  506. aMap.clear();
  507. final int size = input.readInt();
  508. for (int i = 0; i < size; i++) {
  509. CharSequence key = APTSerializeUtils.readFileNameIndex(input, pathManager, unitId);
  510. assert key != null;
  511. final int arraySize = input.readInt();
  512. assert arraySize != 0;
  513. if (arraySize == 1) {
  514. aMap.put(key, APTSerializeUtils.readFileNameIndex(input, pathManager, unitId));
  515. } else {
  516. final CharSequence[] value = new CharSequence[arraySize];
  517. for (int j = 0; j < arraySize; j++) {
  518. CharSequence path = APTSerializeUtils.readFileNameIndex(input, pathManager, unitId);
  519. assert path != null;
  520. value[j] = path;
  521. }
  522. aMap.put(key, value);
  523. }
  524. }
  525. }
  526. //for unit test only
  527. public Map<CharSequence, FileEntry> getFileStorage() {
  528. return new TreeMap<CharSequence, FileEntry>(myFiles);
  529. }
  530. //for unit test only
  531. public Map<CharSequence, Object/*CharSequence or CharSequence[]*/> getCanonicalNames(){
  532. return new TreeMap<CharSequence, Object>(canonicFiles);
  533. }
  534. public static FileEntry createFileEntryForMerge(CharSequence fileKey) {
  535. return new FileEntry(null, null, fileKey, fileKey);
  536. }
  537. /*package*/ static FileEntry createFileEntry(FileImpl fileImpl) {
  538. CharSequence path = getFileKey(fileImpl.getAbsolutePath(), false);
  539. return new FileEntry(fileImpl.getUID(), null, path, path);
  540. }
  541. private static final boolean TRACE = false;
  542. public static final class FileEntry {
  543. private final CsmUID<CsmFile> fileNew;
  544. private final CharSequence canonical;
  545. private volatile Object data; // either StatePair or List<StatePair>
  546. private volatile int modCount;
  547. @SuppressWarnings("unchecked")
  548. private FileEntry (FileSystem fs, RepositoryDataInput input, int unitIndex) throws IOException {
  549. fileNew = UIDObjectFactory.getDefaultFactory().readUID(input);
  550. canonical = PersistentUtils.readFileNameIndex(input, FilePathCache.getManager(), unitIndex);
  551. modCount = input.readInt();
  552. if (input.readBoolean()) {
  553. int cnt = input.readInt();
  554. assert cnt > 0;
  555. if (cnt == 1) {
  556. PreprocessorStatePair pair = readStatePair(fs, input, unitIndex);
  557. if (TRACE && pair != null && !pair.state.isValid()) {
  558. CndUtils.assertTrueInConsole(false, "read INVALID state for ", this.canonical);
  559. }
  560. data = pair;
  561. } else {
  562. data = new ArrayList<PreprocessorStatePair>(cnt);
  563. for (int i = 0; i < cnt; i++) {
  564. PreprocessorStatePair pair = readStatePair(fs, input, unitIndex);
  565. if (TRACE && pair != null && !pair.state.isValid()) {
  566. CndUtils.assertTrueInConsole(false, "read INVALID state for ", this.canonical);
  567. }
  568. ((List<PreprocessorStatePair>) data).add(pair);
  569. }
  570. }
  571. } else {
  572. data = null;
  573. }
  574. }
  575. private FileEntry(CsmUID<CsmFile> fileNew, APTPreprocHandler.State state, CharSequence fileKey, CharSequence canonicalFileKey) {
  576. this.fileNew = fileNew;
  577. this.data = (state == null) ? null : new PreprocessorStatePair(state, FilePreprocessorConditionState.PARSING);
  578. // if (state == null) {
  579. // if (CndUtils.isDebugMode()) {
  580. // CndUtils.assertTrueInConsole(false, "creating null based entry for " + fileKey); // NOI18N
  581. // }
  582. // }
  583. this.canonical = canonicalFileKey;
  584. this.modCount = 0;
  585. }
  586. private void write(final RepositoryDataOutput output, int unitIndex) throws IOException {
  587. UIDObjectFactory.getDefaultFactory().writeUID(fileNew, output);
  588. PersistentUtils.writeFileNameIndex(canonical, output, unitIndex);
  589. output.writeInt(modCount);
  590. Object aData = data;
  591. output.writeBoolean(aData != null);
  592. if (aData != null) {
  593. if(aData instanceof PreprocessorStatePair) {
  594. output.writeInt(1);
  595. PreprocessorStatePair pair = (PreprocessorStatePair) aData;
  596. if (TRACE && !pair.state.isValid()) {
  597. CndUtils.assertTrueInConsole(false, "write INVALID state for ", this.canonical);
  598. }
  599. writeStatePair(output, pair, unitIndex);
  600. } else {
  601. @SuppressWarnings("unchecked")
  602. Collection<PreprocessorStatePair> pairs = (Collection<PreprocessorStatePair>)aData;
  603. output.writeInt(pairs.size());
  604. for (PreprocessorStatePair pair : pairs) {
  605. if (TRACE && pair != null && !pair.state.isValid()) {
  606. CndUtils.assertTrueInConsole(false, "write INVALID state for ", this.canonical);
  607. }
  608. writeStatePair(output, pair, unitIndex);
  609. }
  610. }
  611. }
  612. }
  613. private static PreprocessorStatePair readStatePair(FileSystem fs, RepositoryDataInput input, int unitIndex) throws IOException {
  614. if (input.readBoolean()) {
  615. APTPreprocHandler.State state = null;
  616. if (input.readBoolean()){
  617. state = PersistentUtils.readPreprocState(fs, input, unitIndex);
  618. }
  619. FilePreprocessorConditionState pcState = null;
  620. if (input.readBoolean()){
  621. pcState = new FilePreprocessorConditionState(input);
  622. } else {
  623. pcState = FilePreprocessorConditionState.PARSING;
  624. }
  625. return new PreprocessorStatePair(state, pcState);
  626. }
  627. return null;
  628. }
  629. private static void writeStatePair(RepositoryDataOutput output, PreprocessorStatePair pair, int unitIndex) throws IOException {
  630. output.writeBoolean(pair != null);
  631. if (pair != null) {
  632. output.writeBoolean(pair.state != null);
  633. if (pair.state != null) {
  634. PersistentUtils.writePreprocState(pair.state, output, unitIndex);
  635. }
  636. output.writeBoolean(pair.pcState != FilePreprocessorConditionState.PARSING);
  637. if (pair.pcState != FilePreprocessorConditionState.PARSING) {
  638. pair.pcState.write(output);
  639. }
  640. }
  641. }
  642. public final synchronized int getModCount() {
  643. return modCount;
  644. }
  645. /**
  646. * @return lock under which all sequances read-decide-modify should be done
  647. * get* and replace* methods are synchronize on the same lock
  648. */
  649. public Object getLock() {
  650. return this;
  651. }
  652. /*tests-only*/public synchronized void debugClearState() {
  653. data = null;
  654. }
  655. /**
  656. * This should only be called if we are sure this is THE ONLY correct state:
  657. * e.g., when creating new file, when invalidating state of a *source* (not a header) file, etc
  658. */
  659. public final synchronized void setState(APTPreprocHandler.State state, FilePreprocessorConditionState pcState) {
  660. if (TraceFlags.TRACE_182342_BUG) {
  661. new Exception("setState replacing:\n" + toString()).printStackTrace(System.err); //NOI18N
  662. }
  663. State oldState = null;
  664. if( state != null && ! state.isCleaned() ) {
  665. state = APTHandlersSupport.createCleanPreprocState(state);
  666. }
  667. if ((data instanceof Collection<?>)) {
  668. @SuppressWarnings("unchecked")
  669. Collection<PreprocessorStatePair> states = (Collection<PreprocessorStatePair>) data;
  670. // check how many good old states are there
  671. // and pick a valid one to check that we aren't replacing better state by worse one
  672. int oldGoodStatesCount = 0;
  673. for (PreprocessorStatePair pair : states) {
  674. if (pair.state != null && pair.state.isValid()) {
  675. oldGoodStatesCount++;
  676. if (oldState == null || state.isCompileContext()) {
  677. oldState = state;
  678. }
  679. }
  680. }
  681. if (oldGoodStatesCount > 1) {
  682. if (CndUtils.isDebugMode()) {
  683. StringBuilder sb = new StringBuilder("Attempt to set state while there are multiple states: " + canonical); // NOI18N
  684. for (PreprocessorStatePair pair : states) {
  685. sb.append(String.format("\nvalid: %b context: %b %s", pair.state.isValid(), pair.state.isCompileContext(), pair.pcState)); //NOI18N
  686. }
  687. Utils.LOG.log(Level.SEVERE, sb.toString(), new Exception(sb.toString()));
  688. }
  689. //return;
  690. }
  691. } else if(data instanceof PreprocessorStatePair) {
  692. oldState = ((PreprocessorStatePair) data).state;
  693. }
  694. incrementModCount();
  695. if (oldState != null
  696. && oldState.isValid()
  697. && oldState.isCompileContext()
  698. && !state.isCompileContext()) {
  699. if (CndUtils.isDebugMode()) {
  700. String message = "Replacing correct state to incorrect " + canonical; // NOI18N
  701. Utils.LOG.log(Level.SEVERE, message, new Exception());
  702. }
  703. return;
  704. }
  705. if (TRACE_PP_STATE_OUT) {
  706. System.err.println("\nPut state for file" + canonical + "\n");
  707. System.err.println(state);
  708. }
  709. data = new PreprocessorStatePair(state, pcState);
  710. if (TraceFlags.TRACE_182342_BUG) {
  711. new Exception("setState at the end:\n"+toString()).printStackTrace(System.err); //NOI18N
  712. }
  713. }
  714. public synchronized void setStates(Collection<PreprocessorStatePair> pairs, PreprocessorStatePair yetOneMore) {
  715. if (TraceFlags.TRACE_182342_BUG) {
  716. new Exception("setStates replacing:\n" + toString()).printStackTrace(System.err); //NOI18N
  717. }
  718. incrementModCount();
  719. if (yetOneMore != null && yetOneMore.state != null && !yetOneMore.state.isCleaned()) {
  720. yetOneMore = new PreprocessorStatePair(APTHandlersSupport.createCleanPreprocState(yetOneMore.state), yetOneMore.pcState);
  721. }
  722. // to save memory consumption
  723. if (yetOneMore == null && pairs.size() == 1) {
  724. yetOneMore = pairs.iterator().next();
  725. pairs = Collections.<PreprocessorStatePair>emptyList();
  726. }
  727. if (pairs.isEmpty()) {
  728. assert yetOneMore != null;
  729. data = yetOneMore;
  730. } else {
  731. ArrayList<PreprocessorStatePair> newData = new ArrayList<PreprocessorStatePair>(pairs.size()+1);
  732. newData.addAll(pairs);
  733. if (yetOneMore != null) {
  734. newData.add(yetOneMore);
  735. }
  736. if (TraceFlags.DYNAMIC_TESTS_TRACE) {
  737. for (int i = 0; i < newData.size(); i++) {
  738. PreprocessorStatePair first = newData.get(i);
  739. for (int j = i + 1; j < newData.size(); j++) {
  740. PreprocessorStatePair second = newData.get(j);
  741. if (first.pcState == FilePreprocessorConditionState.PARSING
  742. || second.pcState == FilePreprocessorConditionState.PARSING) {
  743. if (APTHandlersSupport.equalsIgnoreInvalid(first.state, second.state)) {
  744. new Exception("setStates :\n" + newData).printStackTrace(System.err); //NOI18N
  745. }
  746. }
  747. }
  748. }
  749. }
  750. data = newData;
  751. }
  752. if (CndUtils.isDebugMode()) {
  753. checkConsistency();
  754. }
  755. if (TraceFlags.TRACE_182342_BUG) {
  756. new Exception("setStates at the end:\n"+toString()).printStackTrace(System.err); //NOI18N
  757. }
  758. }
  759. private void checkConsistency() {
  760. Collection<PreprocessorStatePair> pairs = getStatePairs();
  761. if (!pairs.isEmpty()) {
  762. boolean alarm = false;
  763. State firstState = null;
  764. FilePreprocessorConditionState firstPCState = null;
  765. boolean first = true;
  766. for (PreprocessorStatePair pair : getStatePairs()) {
  767. if (first) {
  768. first = false;
  769. firstState = pair.state;
  770. firstPCState = pair.pcState;
  771. } else {
  772. if ((firstState == null) != (pair.state == null)) {
  773. alarm = true;
  774. break;
  775. }
  776. if (firstState != null) {
  777. if (firstState.isCompileContext() != pair.state.isCompileContext()) {
  778. alarm = true;
  779. break;
  780. }
  781. if (firstState.isValid() != pair.state.isValid()) {
  782. // there coud be invalidated state which is in parsing phase now
  783. alarm = !firstState.isValid() && (firstPCState != FilePreprocessorConditionState.PARSING);
  784. alarm |= !pair.state.isValid() && (pair.pcState != FilePreprocessorConditionState.PARSING);
  785. if (alarm) {
  786. break;
  787. }
  788. }
  789. }
  790. }
  791. }
  792. if (alarm) {
  793. StringBuilder sb = new StringBuilder("Mixed preprocessor states: " + canonical); // NOI18N
  794. for (PreprocessorStatePair pair : getStatePairs()) {
  795. if (pair.state == null) {
  796. sb.append(String.format(" (null, %s)", pair.pcState)); //NOI18N
  797. } else {
  798. sb.append(String.format(" (valid: %b, context: %b, %s) ", //NOI18N
  799. pair.state.isValid(), pair.state.isCompileContext(), pair.pcState));
  800. }
  801. }
  802. Utils.LOG.log(Level.INFO, sb.toString(), new Exception(sb.toString()));
  803. }
  804. }
  805. }
  806. private synchronized void incrementModCount() {
  807. modCount = (modCount == Integer.MAX_VALUE) ? 0 : modCount+1;
  808. }
  809. @SuppressWarnings("unchecked")
  810. public synchronized void invalidateStates() {
  811. incrementModCount();
  812. if (data != null) {
  813. if (data instanceof PreprocessorStatePair) {
  814. data = createInvalidState((PreprocessorStatePair) data);
  815. } else {
  816. Collection<PreprocessorStatePair> newData = new ArrayList<PreprocessorStatePair>();
  817. for (PreprocessorStatePair pair : (Collection<PreprocessorStatePair>) data) {
  818. newData.add(createInvalidState(pair));
  819. }
  820. data = newData;
  821. }
  822. }
  823. if (TraceFlags.TRACE_182342_BUG) {
  824. new Exception("invalidateStates:\n"+toString()).printStackTrace(System.err); //NOI18N
  825. }
  826. }
  827. @SuppressWarnings("unchecked")
  828. public synchronized void markAsParsingPreprocStates() {
  829. incrementModCount();
  830. if (data != null) {
  831. if (data instanceof PreprocessorStatePair) {
  832. data = createMarkedAsParsingState((PreprocessorStatePair) data);
  833. } else {
  834. Collection<PreprocessorStatePair> newData = new ArrayList<PreprocessorStatePair>();
  835. for (PreprocessorStatePair pair : (Collection<PreprocessorStatePair>) data) {
  836. newData.add(createMarkedAsParsingState(pair));
  837. }
  838. data = newData;
  839. }
  840. }
  841. if (TraceFlags.TRACE_182342_BUG) {
  842. new Exception("invalidateStates:\n" + toString()).printStackTrace(System.err); //NOI18N
  843. }
  844. }
  845. private static PreprocessorStatePair createMarkedAsParsingState(PreprocessorStatePair pair) {
  846. if (pair == null) {
  847. return pair;
  848. } else {
  849. if (pair.state == null) {
  850. return pair;
  851. } else {
  852. return new PreprocessorStatePair(pair.state, FilePreprocessorConditionState.PARSING);
  853. }
  854. }
  855. }
  856. private static PreprocessorStatePair createInvalidState(PreprocessorStatePair pair) {
  857. if (pair == null) {
  858. return pair;
  859. } else {
  860. if (pair.state == null) {
  861. return pair;
  862. } else {
  863. return new PreprocessorStatePair(APTHandlersSupport.createInvalidPreprocState(pair.state), pair.pcState);
  864. }
  865. }
  866. }
  867. public synchronized Collection<PreprocessorStatePair> getStatePairs() {
  868. if (data == null) {
  869. return Collections.<PreprocessorStatePair>emptyList();
  870. } else if(data instanceof PreprocessorStatePair) {
  871. return Collections.singleton((PreprocessorStatePair) data);
  872. } else {
  873. @SuppressWarnings("unchecked")
  874. Collection<PreprocessorStatePair> array = (Collection<PreprocessorStatePair>) data;
  875. return new ArrayList<PreprocessorStatePair>(array);
  876. }
  877. }
  878. public synchronized Collection<APTPreprocHandler.State> getPrerocStates() {
  879. if (data == null) {
  880. return Collections.emptyList();
  881. } else if(data instanceof PreprocessorStatePair) {
  882. return Collections.singleton(((PreprocessorStatePair) data).state);
  883. } else {
  884. @SuppressWarnings("unchecked")
  885. Collection<PreprocessorStatePair> pairs = (Collection<PreprocessorStatePair>) data;
  886. Collection<APTPreprocHandler.State> result = new ArrayList<State>(pairs.size());
  887. for (PreprocessorStatePair pair : pairs) {
  888. result.add(pair.state);
  889. }
  890. return result;
  891. }
  892. }
  893. //for unit test only
  894. public CsmUID<CsmFile> getTestFileUID(){
  895. return fileNew;
  896. }
  897. @Override
  898. public String toString() {
  899. StringBuilder sb = new StringBuilder();
  900. sb.append(fileNew);
  901. sb.append("\nstates:\n"); //NOI18N
  902. for (PreprocessorStatePair pair : getStatePairs()) {
  903. sb.append(pair);
  904. sb.append('\n'); //NOI18N
  905. }
  906. return sb.toString();
  907. }
  908. }
  909. private CharSequence getCanonicalKey(CharSequence fileKey) {
  910. try {
  911. CharSequence res = CndFileSystemProvider.getCanonicalPath(fileSystem, fileKey);
  912. res = FilePathCache.getManager().getString(res);
  913. if (fileKey.equals(res)) {
  914. return fileKey;
  915. }
  916. return res;
  917. } catch (IOException e) {
  918. // skip exception
  919. return fileKey;
  920. }
  921. }
  922. private static CharSequence getCanonicalKey(FileObject fileObject, CharSequence fileKey) {
  923. try {
  924. CharSequence res = CndFileUtils.getCanonicalPath(fileObject);
  925. res = FilePathCache.getManager().getString(res);
  926. if (fileKey.equals(res)) {
  927. return fileKey;
  928. }
  929. return res;
  930. } catch (IOException e) {
  931. // skip exception
  932. return fileKey;
  933. }
  934. }
  935. }