001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.cnd.repository.translator;
042:
043: import java.io.BufferedInputStream;
044: import java.io.BufferedOutputStream;
045: import java.io.DataInput;
046: import java.io.DataInputStream;
047: import java.io.DataOutput;
048: import java.io.DataOutputStream;
049: import java.io.File;
050: import java.io.FileInputStream;
051: import java.io.FileNotFoundException;
052: import java.io.FileOutputStream;
053: import java.io.IOException;
054: import java.io.InputStream;
055: import java.io.OutputStream;
056: import java.util.ArrayList;
057: import java.util.Collection;
058: import java.util.HashSet;
059: import java.util.Map;
060: import java.util.Set;
061: import java.util.concurrent.ConcurrentHashMap;
062: import java.util.concurrent.CopyOnWriteArraySet;
063: import org.netbeans.modules.cnd.utils.cache.FilePathCache;
064: import org.netbeans.modules.cnd.repository.api.RepositoryTranslation;
065: import org.netbeans.modules.cnd.repository.disk.StorageAllocator;
066: import org.netbeans.modules.cnd.repository.testbench.Stats;
067: import org.netbeans.modules.cnd.repository.util.IntToStringCache;
068:
069: /**
070: * This class is responsible for int <-> String translation for both
071: * 1) file names
072: * 2) unit names
073: * It is also responsible for master index processing
074: * and the required units verification.
075: *
076: * The required units issue is caused by the following two circumstances:
077: * a) Each unit stores its own int to string table that is used for decoding its keys
078: * b) A unit can store other units (required units) keys
079: *
080: * By required units verification we prevent the following situation
081: * (which otherwise causes a huge mess).
082: * Consider two projects, App1, App2 and a library Lib * that is required for both App1 and App2.
083: * User performs the following steps:
084: * 1) Opens App1 - App1 and Lib persistence is created. Then closes IDE.
085: * 2) Now Lib persistence is erased (well, if user makes this by hands, we aren't responsible...
086: * but it can happen if user opens Lib and IDE exits abnormally - then upon Lib reopen
087: * its persistence is erased)
088: * 3) User opens App2 - Lib persistence is recreated
089: * 4) User opens App1 again.
090: * Now App1 contains Lin keys, but Lib now contain int/string tables that are quite different!!!
091: *
092: * To prevent this situation, the following algorithm is used:
093: * - Each unit's int/string table has a timestamp of its creation.
094: * - When a unit closes, it stores all reqiured units timestams.
095: * - When a unit opens, it checks that required inits timestamps are the same
096: * as they were at closure. Otherwise persistence is invalidated (erased)
097: * for main unit and requires units.
098: *
099: * @author Nickolay Dalmatov
100: */
101: public class RepositoryTranslatorImpl implements RepositoryTranslation {
102:
103: /**
104: * It is
105: * 1) an int/string table of the unit names
106: * 2) a container for int/string table for each unit's file names (a table per unit)
107: * (stores units timestamps as well)
108: */
109: private static UnitsCache unitNamesCache = new UnitsCache();
110:
111: private static boolean loaded = false;
112:
113: private static final int DEFAULT_VERSION_OF_PERSISTENCE_MECHANIZM = 0;
114: private static int version = DEFAULT_VERSION_OF_PERSISTENCE_MECHANIZM;
115:
116: private final static String MASTER_INDEX_FILE_NAME = System
117: .getProperty("netbeans.user")
118: + //NOI18N
119: File.separator
120: + "var"
121: + File.separator
122: + "cache"
123: + File.separator + "cnd-projects-index"; //NOI18N
124:
125: private final static String PROJECT_INDEX_FILE_NAME = "project-index"; //NOI18N
126:
127: /** Creates a new instance of RepositoryTranslatorImpl */
128: public RepositoryTranslatorImpl() {
129: // load master index
130: }
131:
132: public int getFileIdByName(final int unitId, final String fileName) {
133: assert fileName != null;
134: final IntToStringCache unitFileNames = getUnitFileNames(unitId);
135: return unitFileNames.getId(fileName);
136: }
137:
138: public String getFileNameById(final int unitId, final int fileId) {
139: final IntToStringCache fileNames = getUnitFileNames(unitId);
140: final String fileName = fileNames.getValueById(fileId);
141: return fileName;
142: }
143:
144: public String getFileNameByIdSafe(final int unitId, final int fileId) {
145: final IntToStringCache fileNames = getUnitFileNames(unitId);
146: final String fileName = fileNames.containsId(fileId) ? fileNames
147: .getValueById(fileId)
148: : "?"; // NOI18N
149: return fileName;
150: }
151:
152: public int getUnitId(String unitName) {
153: if (!unitNamesCache.containsValue(unitName)) {
154: // NB: this unit can't be open (since there is no such unit in unitNamesCache)
155: // so we are just removing some ocassionally existing in persisntence files
156: StorageAllocator.getInstance().deleteUnitFiles(unitName,
157: false);
158: }
159: return unitNamesCache.getId(unitName);
160: }
161:
162: public String getUnitName(int unitId) {
163: return unitNamesCache.getValueById(unitId);
164: }
165:
166: private static IntToStringCache getUnitFileNames(int unitId) {
167: return unitNamesCache.getFileNames(unitId);
168: }
169:
170: /**
171: * Reads master index -
172: * a list if
173: * - units
174: * - their timestamps
175: * - their required units
176: * - required units timestamps
177: */
178: private static void readMasterIndex(DataInput stream)
179: throws IOException {
180: assert stream != null;
181: unitNamesCache = new UnitsCache(stream);
182: }
183:
184: private static void writeUnitsCache(DataOutput stream)
185: throws IOException {
186: assert stream != null;
187:
188: unitNamesCache.write(stream);
189: }
190:
191: private static boolean readUnitFilesCache(String name,
192: DataInput stream, Set<String> antiLoop) throws IOException {
193: assert name != null;
194: assert stream != null;
195:
196: IntToStringCache filesCache = new IntToStringCache(stream);
197: if (Stats.TRACE_REPOSITORY_TRANSLATOR)
198: trace("Read unit files cache for %s ts=%d\n", name,
199: filesCache.getTimestamp()); // NOI18N
200: if ((filesCache.getVersion() == version)
201: && UnitsCache.validateReqUnits(name, antiLoop)) {
202: unitNamesCache.insertUnitFileCache(name, filesCache);
203: return true;
204: } else {
205: filesCache = new IntToStringCache();
206: if (Stats.TRACE_REPOSITORY_TRANSLATOR)
207: trace(
208: "Req. units validation failed for %s. Setting ts=%d\n",
209: name, filesCache.getTimestamp()); // NOI18N
210: unitNamesCache.insertUnitFileCache(name, filesCache);
211: }
212:
213: return false;
214: }
215:
216: private static void writeUnitFilesCache(String unitName,
217: DataOutput stream) throws IOException {
218: assert unitName != null;
219: assert stream != null;
220:
221: int unitId = unitNamesCache.getId(unitName);
222: IntToStringCache filesCache = unitNamesCache
223: .getFileNames(unitId);
224: filesCache.write(stream);
225: }
226:
227: public static void closeUnit(String unitName,
228: Set<String> requiredUnits) {
229: UnitsCache.updateReqUnitInfo(unitName, requiredUnits);
230: storeUnitIndex(unitName);
231: unitNamesCache.removeFileNames(unitName);
232: }
233:
234: public static void shutdown() {
235: storeMasterIndex();
236: }
237:
238: public static void loadUnitIndex(final String unitName) {
239: loadUnitIndex(unitName, new HashSet<String>());
240: }
241:
242: private static void loadUnitIndex(final String unitName,
243: Set<String> antiLoop) {
244: // check if the index is already loaded
245: if (UnitsCache.isUnitIndexLoaded(unitName)) {
246: return;
247: }
248: InputStream fis = null;
249: InputStream bis = null;
250: DataInputStream dis = null;
251: String unitIndexFileName = getUnitIndexName(unitName);
252: boolean indexLoaded = false;
253:
254: try {
255: // don't produce exceptions when it's clear that the file just doesn't exist
256: if (new File(unitIndexFileName).exists()) {
257: fis = new FileInputStream(unitIndexFileName);
258: bis = new BufferedInputStream(fis);
259: dis = new DataInputStream(bis);
260: indexLoaded = readUnitFilesCache(unitName, dis,
261: antiLoop);
262: }
263: } catch (FileNotFoundException e) {
264: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
265: e.printStackTrace();
266: }
267: } catch (IOException e) {
268: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
269: e.printStackTrace();
270: }
271: } catch (Throwable tr) {
272: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
273: tr.printStackTrace();
274: }
275: } finally {
276: if (dis != null) {
277: try {
278: dis.close();
279: new File(unitIndexFileName).delete();
280: } catch (IOException e) {
281: e.printStackTrace();
282: }
283: }
284: }
285:
286: if (!indexLoaded) {
287: StorageAllocator.getInstance().deleteUnitFiles(unitName,
288: false);
289: unitNamesCache.cleanUnitData(unitName);
290: // System.err.println("loadUnitIndex: unit file deleted for " + unitName);
291: }
292: }
293:
294: private static String getUnitIndexName(final String unitName) {
295: return StorageAllocator.getInstance().getUnitStorageName(
296: unitName)
297: + PROJECT_INDEX_FILE_NAME;
298: }
299:
300: public static void removeUnit(final String unitName) {
301: File file = new File(getUnitIndexName(unitName));
302: file.delete();
303: }
304:
305: private static void storeUnitIndex(final String unitName) {
306: OutputStream fos = null;
307: OutputStream bos = null;
308: DataOutputStream dos = null;
309: String unitIndexFileName = getUnitIndexName(unitName);
310: boolean indexStored = false;
311: try {
312: fos = new FileOutputStream(unitIndexFileName, false);
313: bos = new BufferedOutputStream(fos);
314: dos = new DataOutputStream(bos);
315: writeUnitFilesCache(unitName, dos);
316: indexStored = true;
317: } catch (FileNotFoundException e) {
318: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
319: e.printStackTrace();
320: }
321: } catch (IOException e) {
322: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
323: e.printStackTrace();
324: }
325: } catch (Throwable tr) {
326: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
327: tr.printStackTrace();
328: }
329: } finally {
330: try {
331: if (dos != null) {
332: dos.close();
333: }
334: } catch (IOException e) {
335: e.printStackTrace();
336: }
337: }
338: if (!indexStored) {
339: StorageAllocator.getInstance().deleteUnitFiles(unitName,
340: false);
341: // System.err.println("storeUnitIndex: unit file deleted for " + unitName);
342: }
343: }
344:
345: /**
346: * Loads master index and unit/timestamp pairs
347: */
348: private static void loadMasterIndex() {
349: InputStream fis = null;
350: InputStream bis = null;
351: DataInputStream dis = null;
352: try {
353: fis = new FileInputStream(MASTER_INDEX_FILE_NAME);
354: bis = new BufferedInputStream(fis);
355: dis = new DataInputStream(bis);
356: readMasterIndex(dis);
357: } catch (FileNotFoundException e) {
358: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
359: e.printStackTrace();
360: }
361: } catch (IOException e) {
362: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
363: e.printStackTrace();
364: }
365: } catch (Throwable tr) {
366: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
367: tr.printStackTrace();
368: }
369: } finally {
370: if (dis != null) {
371: try {
372: dis.close();
373: } catch (IOException e) {
374: }
375: }
376: }
377: }
378:
379: public static void storeMasterIndex() {
380: OutputStream fos = null;
381: OutputStream bos = null;
382: DataOutputStream dos = null;
383:
384: try {
385: fos = new FileOutputStream(MASTER_INDEX_FILE_NAME, false);
386: bos = new BufferedOutputStream(fos);
387: dos = new DataOutputStream(bos);
388: writeUnitsCache(dos);
389: } catch (FileNotFoundException e) {
390: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
391: e.printStackTrace();
392: }
393: } catch (IOException e) {
394: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
395: e.printStackTrace();
396: }
397: } catch (Throwable tr) {
398: if (Stats.TRACE_REPOSITORY_TRANSLATOR) {
399: tr.printStackTrace();
400: }
401: } finally {
402: try {
403: if (dos != null) {
404: dos.close();
405: }
406: } catch (IOException e) {
407: }
408: }
409: }
410:
411: public static void startup(int newVersion) {
412: version = newVersion;
413: if (!loaded) {
414: synchronized (unitNamesCache) {
415: if (!loaded) {
416: loaded = true;
417: loadMasterIndex();
418: }
419: }
420: }
421: }
422:
423: public static int getVersion() {
424: return version;
425: }
426:
427: ////////////////////////////////////////////////////////////////////////////
428: // impl details
429:
430: /**
431: * Just a structure that holds name and stimestamp
432: * for required unit
433: */
434: private static class RequiredUnit {
435: private String unitName;
436: private long timestamp;
437:
438: public RequiredUnit(String name, long time) {
439: unitName = name;
440: timestamp = time;
441: }
442:
443: public RequiredUnit(DataInput stream) throws IOException {
444: unitName = stream.readUTF();
445: timestamp = stream.readLong();
446: }
447:
448: public void write(DataOutput stream) throws IOException {
449: stream.writeUTF(unitName);
450: stream.writeLong(timestamp);
451: }
452:
453: public String getName() {
454: return unitName;
455: }
456:
457: public long getTimestamp() {
458: return timestamp;
459: }
460: }
461:
462: /**
463: * This class
464: * 1) Acts as a int/string table for unit names;
465: * 2) Contains int/string tables for file names (a table per unit);
466: */
467: private static class UnitsCache extends IntToStringCache {
468:
469: /**
470: * A list of int/string tables for unitsm a table per unit.
471: * It is "parallel" to the super.cache array.
472: *
473: * Since it has an entry *for each unit in the persistence*
474: * (not only for open units),
475: * the super.cache contains empty instances of IntToStringCache
476: * (for units that are not yet open)
477: */
478: private static ArrayList<IntToStringCache> fileNamesCaches = new ArrayList<IntToStringCache>();
479:
480: /**
481: * Stores unit timestamps.
482: * For open units they (as I understand) coincide with
483: * fileNamesCaches[idx].getTimestamp().
484: * But for units that are not open, the correct value is in unit2timestamp map.
485: */
486: private static Map<String, Long> unit2timestamp = new ConcurrentHashMap<String, Long>();
487:
488: /**
489: * Maps a unit to its required units
490: */
491: private static Map<String, Collection<RequiredUnit>> unit2requnint = new ConcurrentHashMap<String, Collection<RequiredUnit>>();
492:
493: /**
494: * Is called before closing a unit.
495: * Stores the list of required inits and their timestamps.
496: */
497: // package-local
498: static void updateReqUnitInfo(String unitName,
499: Set<String> reqUnits) {
500: // update timestamps
501: // ???! why do we copy the timestamps from unitNamesCache.cache to unit2timestamp for ALL modules???
502: for (int i = 0; i < unitNamesCache.cache.size(); i++) { // unitNamesCache AKA this
503: String uName = unitNamesCache.cache.get(i);
504: long uTs = fileNamesCaches.get(i).getTimestamp();
505: unit2timestamp.put(uName, uTs);
506: }
507: // store required units set
508: Collection<RequiredUnit> unitReqUnits = new CopyOnWriteArraySet<RequiredUnit>();
509: if (reqUnits != null) {
510: for (String rUnitName : reqUnits) {
511: long ts = unit2timestamp.get(rUnitName).longValue();
512: RequiredUnit rU = new RequiredUnit(rUnitName, ts);
513: unitReqUnits.add(rU);
514: }
515: }
516: unit2requnint.put(unitName, unitReqUnits);
517: }
518:
519: /**
520: * Validates required units -
521: * checks at least one required unit was re-created off-line.
522: * If so, the cache of the unit that was re-created
523: * and the cache of the unit that depends on it
524: * should be invalidated
525: * @param unitName name of the unit top check
526: * @param antiLoop prevents infinite recursion (in the case of cyclic dependencies)
527: * @return
528: */
529: private static boolean validateReqUnits(String unitName,
530: Set<String> antiLoop) {
531: if (antiLoop.contains(unitName)) {
532: return true;
533: }
534: antiLoop.add(unitName);
535: boolean result = true;
536: Collection<RequiredUnit> reqUnits = unit2requnint
537: .get(unitName);
538: for (RequiredUnit rU : reqUnits) {
539:
540: if (!isUnitIndexLoaded(rU.getName())) {
541: RepositoryTranslatorImpl.loadUnitIndex(
542: rU.getName(), antiLoop);
543: }
544:
545: Long tsL = unit2timestamp.get(rU.getName());
546: if (tsL != null) {
547: long ts = tsL.longValue();
548: if (ts != rU.getTimestamp()) {
549: if (Stats.TRACE_REPOSITORY_TRANSLATOR)
550: trace(
551: "Req. unit validation FAILED for %s: ts(unit2timestamp)=%d, ts(unit2requnint)=%s \n",
552: unitName, ts, rU.getTimestamp()); // NOI18N
553: result = false;
554: break;
555: }
556: } else {
557: if (Stats.TRACE_REPOSITORY_TRANSLATOR)
558: trace(
559: "Req. unit validation FAILED for %s: ts=NULL\n",
560: unitName); // NOI18N
561: result = false;
562: break;
563: }
564: }
565: return result;
566: }
567:
568: /**
569: * For the given unit,
570: * stores the collection of it's required units
571: * (instances of RequiredUnit)
572: */
573: private static void writeRequiredUnits(String unitName,
574: DataOutput stream) throws IOException {
575: assert unitName != null;
576: assert stream != null;
577:
578: Collection<RequiredUnit> rUnits = unit2requnint
579: .get(unitName);
580: assert rUnits != null;
581:
582: int size = rUnits.size();
583: stream.writeInt(size);
584: for (RequiredUnit unit : rUnits) {
585: unit.write(stream);
586: if (Stats.TRACE_REPOSITORY_TRANSLATOR)
587: trace("\t\treq.unit %s ts=%d\n", unit.getName(),
588: unit.getTimestamp()); //NOI18N
589: }
590: }
591:
592: /**
593: * Reads the collection of units (instances of RequiredUnit)
594: */
595: private static Collection<RequiredUnit> readRequiredUnits(
596: DataInput stream) throws IOException {
597: assert stream != null;
598: Collection<RequiredUnit> units = new CopyOnWriteArraySet<RequiredUnit>();
599: int size = stream.readInt();
600:
601: for (int i = 0; i < size; i++) {
602: RequiredUnit unit = new RequiredUnit(stream);
603: units.add(unit);
604: if (Stats.TRACE_REPOSITORY_TRANSLATOR)
605: trace("\t\tRead req. unit %s ts=%d\n", unit
606: .getName(), unit.getTimestamp()); // NOI18N
607: }
608: return units;
609: }
610:
611: @Override
612: public void write(DataOutput stream) throws IOException {
613: assert cache != null;
614: assert stream != null;
615:
616: stream.writeInt(version);
617: stream.writeLong(timestamp);
618:
619: int size = cache.size();
620: stream.writeInt(size);
621:
622: if (Stats.TRACE_REPOSITORY_TRANSLATOR)
623: trace("Storing master index; size=%d\n", size); //NOI18N
624:
625: for (int i = 0; i < size; i++) {
626: String value = cache.get(i);
627: stream.writeUTF(value);
628: stream.writeLong(unit2timestamp.get(value).longValue());
629: if (Stats.TRACE_REPOSITORY_TRANSLATOR)
630: trace("\tUnit %s ts=%d\n", value, unit2timestamp
631: .get(value)); //NOI18N
632: writeRequiredUnits(value, stream);
633: }
634: }
635:
636: private static boolean isUnitIndexLoaded(final String unitName) {
637: if (!unitNamesCache.cache.contains(unitName)) {
638: return false;
639: }
640: int id = unitNamesCache.cache.indexOf(unitName);
641:
642: if (fileNamesCaches.get(id).size() != 0) { //incorrect! there might be 0 files!
643:
644: return true;
645: }
646: return false;
647: }
648:
649: public UnitsCache() {
650:
651: }
652:
653: /**
654: * Reads master index.
655: * Fills fileNamesCaches with an empty int/string tables.
656: */
657: public UnitsCache(DataInput stream) throws IOException {
658:
659: assert stream != null;
660: assert cache != null;
661:
662: cache.clear();
663: fileNamesCaches.clear();
664:
665: stream.readInt();
666: stream.readLong();
667:
668: int size = stream.readInt();
669:
670: if (Stats.TRACE_REPOSITORY_TRANSLATOR)
671: trace("Reading master index (%d) elements\n", size); // NOI18N
672:
673: for (int i = 0; i < size; i++) {
674:
675: String value = FilePathCache
676: .getString(stream.readUTF()).toString();
677: cache.add(value);
678:
679: // timestamp from master index -> unit2timestamp
680: long ts = stream.readLong();
681: unit2timestamp.put(value, Long.valueOf(ts));
682:
683: if (Stats.TRACE_REPOSITORY_TRANSLATOR)
684: trace("\tRead %s ts=%d\n", value, ts); // NOI18N
685:
686: // req. units list from master index -> unit2requnint
687: // (with timestamps from master index)
688: unit2requnint.put(value, readRequiredUnits(stream));
689:
690: // new dummy int/string cache (with the current (???!) timestamp
691: fileNamesCaches.add(new IntToStringCache(ts));
692: }
693: }
694:
695: public void insertUnitFileCache(String name,
696: IntToStringCache filesCache) {
697: int index = cache.indexOf(name);
698: if (index == -1) {
699: index = super .makeId(name);
700: }
701: fileNamesCaches.set(index, filesCache);
702: unit2timestamp.put(name, Long.valueOf(filesCache
703: .getTimestamp()));
704: unit2requnint.put(name,
705: new CopyOnWriteArraySet<RequiredUnit>());
706: }
707:
708: public IntToStringCache removeFileNames(String unitName) {
709: IntToStringCache fileNames = null;
710: int index = cache.indexOf(unitName);
711: if (index != -1) {
712: fileNames = fileNamesCaches.get(index);
713: long ts = fileNames.getTimestamp();
714: unit2timestamp.put(unitName, ts);
715: fileNamesCaches.set(index, new IntToStringCache(ts));
716: }
717: return fileNames;
718: }
719:
720: /**
721: * synchronization is controlled by calling getId() method
722: */
723: @Override
724: protected int makeId(String unitName) {
725: int id = cache.indexOf(null);
726: IntToStringCache fileCache = new IntToStringCache();
727:
728: if (id == -1) {
729: id = super .makeId(unitName);
730: fileNamesCaches.add(fileCache);
731: } else {
732: cache.set(id, unitName);
733: fileNamesCaches.set(id, fileCache);
734: }
735:
736: assert fileNamesCaches.size() == cache.size();
737:
738: unit2requnint.put(unitName,
739: new CopyOnWriteArraySet<RequiredUnit>());
740: unit2timestamp.put(unitName, fileCache.getTimestamp());
741:
742: return id;
743: }
744:
745: /**
746: * no synchronization is set to speed up processing
747: * this call is safe due to add-only way of work with
748: * List
749: */
750: public IntToStringCache getFileNames(int unitId) {
751: return fileNamesCaches.get(unitId);
752: }
753:
754: private void cleanUnitData(String unitName) {
755: IntToStringCache fileCache = new IntToStringCache();
756: unit2requnint.put(unitName,
757: new CopyOnWriteArraySet<RequiredUnit>());
758: unit2timestamp.put(unitName, fileCache.getTimestamp());
759: }
760: }
761:
762: private static void trace(String format, Object... args) {
763: Object[] newArgs = new Object[args.length + 1];
764: newArgs[0] = Long.valueOf(System.currentTimeMillis());
765: for (int i = 0; i < args.length; i++) {
766: newArgs[i + 1] = args[i];
767: }
768: System.err.printf("RepositoryTranslator [%d] " + format,
769: newArgs);
770: }
771:
772: }
|