update copyrights
[idea/community.git] / platform / lang-api / src / com / intellij / psi / stubs / SerializationManagerImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * @author max
19  */
20 package com.intellij.psi.stubs;
21
22 import com.intellij.openapi.application.PathManager;
23 import com.intellij.openapi.components.ApplicationComponent;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.extensions.Extensions;
26 import com.intellij.openapi.util.io.FileUtil;
27 import com.intellij.psi.tree.IElementType;
28 import com.intellij.psi.tree.IStubFileElementType;
29 import com.intellij.util.io.DataInputOutputUtil;
30 import com.intellij.util.io.PersistentStringEnumerator;
31 import org.jetbrains.annotations.NotNull;
32
33 import java.io.*;
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.concurrent.atomic.AtomicBoolean;
39
40 public class SerializationManagerImpl extends SerializationManager implements ApplicationComponent {
41   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.stubs.SerializationManagerImpl");
42
43   private PersistentStringEnumerator myNameStorage;
44
45   private final Map<Integer, StubSerializer<? extends StubElement>> myIdToSerializer = new HashMap<Integer, StubSerializer<? extends StubElement>>();
46   private final Map<StubSerializer<? extends StubElement>, Integer> mySerializerToId = new HashMap<StubSerializer<? extends StubElement>, Integer>();
47   private final List<StubSerializer<? extends StubElement>> myAllSerializers = new ArrayList<StubSerializer<? extends StubElement>>();
48   private final AtomicBoolean myNameStorageCrashed = new AtomicBoolean(false);
49   private final File myFile = new File(PathManager.getSystemPath() + "/index/rep.names");
50   private boolean mySerializersLoaded = false;
51
52   public SerializationManagerImpl() {
53     myFile.getParentFile().mkdirs();
54     try {
55       myNameStorage = new PersistentStringEnumerator(myFile);
56     }
57     catch (IOException e) {
58       myNameStorageCrashed.set(true);
59       LOG.info(e);
60       repairNameStorage(); // need this in order for myNameStorage not to be null
61       myNameStorageCrashed.set(true);
62     }
63     registerSerializer(PsiFileStubImpl.TYPE);
64   }
65
66   public boolean isNameStorageCorrupted() {
67     return myNameStorageCrashed.get();
68   }
69
70   public void repairNameStorage() {
71     if (myNameStorageCrashed.getAndSet(false)) {
72       try {
73         if (myNameStorage != null) {
74           myNameStorage.close();
75         }
76
77         final File[] files = myFile.getParentFile().listFiles();
78         if (files != null) {
79           for (File file : files) {
80             if (file.getName().startsWith(myFile.getName())) {
81               FileUtil.delete(file);
82             }
83           }
84         }
85         myNameStorage = new PersistentStringEnumerator(myFile);
86         
87         mySerializerToId.clear();
88         myIdToSerializer.clear();
89         for (StubSerializer<? extends StubElement> serializer : myAllSerializers) {
90           assignId(serializer);
91         }
92       }
93       catch (IOException e) {
94         LOG.info(e);
95         myNameStorageCrashed.set(true);
96       }
97     }
98   }
99
100   public void registerSerializer(@NotNull StubSerializer<? extends StubElement> serializer) {
101     myAllSerializers.add(serializer);
102     try {
103       assignId(serializer);
104     }
105     catch (IOException e) {
106       LOG.info(e);
107       myNameStorageCrashed.set(true);
108     }
109   }
110
111   private void assignId(@NotNull final StubSerializer<? extends StubElement> serializer) throws IOException {
112     final int id = persistentId(serializer);
113     final StubSerializer old = myIdToSerializer.put(id, serializer);
114     assert old == null : "ID: " + serializer.getExternalId() + " is not unique";
115
116     final Integer oldId = mySerializerToId.put(serializer, id);
117     assert oldId == null : "Serializer " + serializer + " is already registered";
118   }
119
120   private int persistentId(@NotNull final StubSerializer<? extends StubElement> serializer) throws IOException {
121     return myNameStorage.enumerate(serializer.getExternalId());
122   }
123
124   private synchronized void initSerializers() {
125     if (mySerializersLoaded) return;
126     mySerializersLoaded = true;
127     for(StubElementTypeHolderEP holderEP: Extensions.getExtensions(StubElementTypeHolderEP.EP_NAME)) {
128       holderEP.initialize();
129     }
130     final IElementType[] stubElementTypes = IElementType.enumerate(new IElementType.Predicate() {
131       public boolean matches(final IElementType type) {
132         return type instanceof StubSerializer;
133       }
134     });
135     for(IElementType type: stubElementTypes) {
136       if (type instanceof IStubFileElementType && ((IStubFileElementType) type).getExternalId().equals(PsiFileStubImpl.TYPE.getExternalId())) {
137         continue;
138       }
139       StubSerializer stubSerializer = (StubSerializer) type;
140
141       if (!myAllSerializers.contains(stubSerializer)) {
142         registerSerializer(stubSerializer);
143       }
144     }
145   }
146
147   public void serialize(StubElement rootStub, OutputStream stream) {
148     StubOutputStream stubOutputStream = new StubOutputStream(stream, myNameStorage);
149     doSerialize(rootStub, stubOutputStream);
150   }
151
152   private void doSerialize(final StubElement rootStub, final StubOutputStream stream) {
153     initSerializers();
154     try {
155       final StubSerializer serializer = getSerializer(rootStub);
156
157       DataInputOutputUtil.writeINT(stream, getClassId(serializer));
158       serializer.serialize(rootStub, stream);
159
160       final List<StubElement> children = rootStub.getChildrenStubs();
161       DataInputOutputUtil.writeINT(stream, children.size());
162       for (StubElement child : children) {
163         doSerialize(child, stream);
164       }
165     }
166     catch (IOException e) {
167       LOG.info(e);
168       myNameStorageCrashed.set(true);
169     }
170   }
171
172   public StubSerializer getSerializer(final StubElement rootStub) {
173     if (rootStub instanceof PsiFileStub) {
174       final PsiFileStub fileStub = (PsiFileStub)rootStub;
175       return fileStub.getType();
176     }
177
178     return rootStub.getStubType();
179   }
180
181   public StubElement deserialize(InputStream stream) {
182     StubInputStream inputStream = new StubInputStream(stream, myNameStorage);
183     initSerializers();
184     try {
185       return deserialize(inputStream, null);
186     }
187     catch (IOException e) {
188       myNameStorageCrashed.set(true);
189       LOG.info(e);
190       throw new RuntimeException(e);
191     }
192   }
193
194   private StubElement deserialize(StubInputStream stream, StubElement parentStub) throws IOException {
195     final int id = DataInputOutputUtil.readINT(stream);
196     final StubSerializer serializer = getClassById(id);
197     
198     assert serializer != null : "No serializer registered for stub: ID=" + id + "; parent stub class=" + (parentStub != null? parentStub.getClass().getName() : "null");
199     
200     StubElement stub = serializer.deserialize(stream, parentStub);
201     int childCount = DataInputOutputUtil.readINT(stream);
202     for (int i = 0; i < childCount; i++) {
203       deserialize(stream, stub);
204     }
205     return stub;
206   }
207
208   private int getClassId(final StubSerializer serializer) {
209     final Integer idValue = mySerializerToId.get(serializer);
210     assert idValue != null: "No ID found for serializer " + serializer;
211     return idValue.intValue();
212   }
213
214   private StubSerializer getClassById(int id) {
215     return myIdToSerializer.get(id);
216   }
217
218   @NotNull
219   public String getComponentName() {
220     return "PSI.SerializationManager";
221   }
222
223   public void initComponent() {
224   }
225
226   public void disposeComponent() {
227     try {
228       myNameStorage.close();
229     }
230     catch (IOException e) {
231       LOG.error(e);
232     }
233   }
234 }
235