jps model: cache role instances to optimize memory usage
[idea/community.git] / jps / model-impl / src / org / jetbrains / jps / model / library / impl / JpsLibraryImpl.java
1 /*
2  * Copyright 2000-2015 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 package org.jetbrains.jps.model.library.impl;
17
18 import com.intellij.openapi.util.io.FileUtil;
19 import com.intellij.openapi.util.io.FileUtilRt;
20 import com.intellij.util.containers.ContainerUtil;
21 import org.jetbrains.annotations.NotNull;
22 import org.jetbrains.annotations.Nullable;
23 import org.jetbrains.jps.model.*;
24 import org.jetbrains.jps.model.ex.JpsElementCollectionRole;
25 import org.jetbrains.jps.model.ex.JpsNamedCompositeElementBase;
26 import org.jetbrains.jps.model.impl.JpsElementCollectionImpl;
27 import org.jetbrains.jps.model.library.*;
28 import org.jetbrains.jps.util.JpsPathUtil;
29
30 import java.io.File;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Set;
35 import java.util.concurrent.ConcurrentMap;
36
37 /**
38  * @author nik
39  */
40 public class JpsLibraryImpl<P extends JpsElement> extends JpsNamedCompositeElementBase<JpsLibraryImpl<P>> implements JpsTypedLibrary<P> {
41   private static final ConcurrentMap<JpsOrderRootType, JpsElementCollectionRole<JpsLibraryRoot>> ourRootRoles = ContainerUtil.newConcurrentMap();
42   private final JpsLibraryType<P> myLibraryType;
43
44   public JpsLibraryImpl(@NotNull String name, @NotNull JpsLibraryType<P> type, @NotNull P properties) {
45     super(name);
46     myLibraryType = type;
47     myContainer.setChild(myLibraryType.getPropertiesRole(), properties);
48   }
49
50   private JpsLibraryImpl(@NotNull JpsLibraryImpl<P> original) {
51     super(original);
52     myLibraryType = original.myLibraryType;
53   }
54
55   @Override
56   @NotNull
57   public JpsLibraryType<P> getType() {
58     return myLibraryType;
59   }
60
61   @Nullable
62   @Override
63   public <P extends JpsElement> JpsTypedLibrary<P> asTyped(@NotNull JpsLibraryType<P> type) {
64     //noinspection unchecked
65     return myLibraryType.equals(type) ? (JpsTypedLibrary<P>)this : null;
66   }
67
68   @NotNull
69   @Override
70   public P getProperties() {
71     return myContainer.getChild(myLibraryType.getPropertiesRole());
72   }
73
74   @NotNull
75   @Override
76   public List<JpsLibraryRoot> getRoots(@NotNull JpsOrderRootType rootType) {
77     final JpsElementCollection<JpsLibraryRoot> rootsCollection = myContainer.getChild(getRole(rootType));
78     return rootsCollection != null ? rootsCollection.getElements() : Collections.<JpsLibraryRoot>emptyList();
79   }
80
81   @Override
82   public void addRoot(@NotNull String url, @NotNull JpsOrderRootType rootType) {
83     addRoot(url, rootType, JpsLibraryRoot.InclusionOptions.ROOT_ITSELF);
84   }
85
86   @Override
87   public void addRoot(@NotNull File file, @NotNull JpsOrderRootType rootType) {
88     addRoot(JpsPathUtil.getLibraryRootUrl(file), rootType);
89   }
90
91   @Override
92   public void addRoot(@NotNull final String url, @NotNull final JpsOrderRootType rootType,
93                       @NotNull JpsLibraryRoot.InclusionOptions options) {
94     myContainer.getOrSetChild(getRole(rootType)).addChild(new JpsLibraryRootImpl(url, rootType, options));
95   }
96
97   @Override
98   public void removeUrl(@NotNull final String url, @NotNull final JpsOrderRootType rootType) {
99     final JpsElementCollection<JpsLibraryRoot> rootsCollection = myContainer.getChild(getRole(rootType));
100     if (rootsCollection != null) {
101       for (JpsLibraryRoot root : rootsCollection.getElements()) {
102         if (root.getUrl().equals(url) && root.getRootType().equals(rootType)) {
103           rootsCollection.removeChild(root);
104           break;
105         }
106       }
107     }
108   }
109
110   private static JpsElementCollectionRole<JpsLibraryRoot> getRole(JpsOrderRootType type) {
111     JpsElementCollectionRole<JpsLibraryRoot> role = ourRootRoles.get(type);
112     if (role != null) return role;
113     ourRootRoles.putIfAbsent(type, JpsElementCollectionRole.create(new JpsLibraryRootRole(type)));
114     return ourRootRoles.get(type);
115   }
116
117   @Override
118   public void delete() {
119     getParent().removeChild(this);
120   }
121
122   public JpsElementCollectionImpl<JpsLibrary> getParent() {
123     //noinspection unchecked
124     return (JpsElementCollectionImpl<JpsLibrary>)myParent;
125   }
126
127   @NotNull
128   @Override
129   public JpsLibraryImpl<P> createCopy() {
130     return new JpsLibraryImpl<P>(this);
131   }
132
133   @NotNull
134   @Override
135   public JpsLibraryReference createReference() {
136     return new JpsLibraryReferenceImpl(getName(), createParentReference());
137   }
138
139   private JpsElementReference<JpsCompositeElement> createParentReference() {
140     //noinspection unchecked
141     return ((JpsReferenceableElement<JpsCompositeElement>)getParent().getParent()).createReference();
142   }
143
144   @Override
145   public List<File> getFiles(final JpsOrderRootType rootType) {
146     List<String> urls = getRootUrls(rootType);
147     List<File> files = new ArrayList<File>(urls.size());
148     for (String url : urls) {
149       if (!url.startsWith("jrt://")) {
150         files.add(JpsPathUtil.urlToFile(url));
151       }
152     }
153     return files;
154   }
155
156   @Override
157   public List<String> getRootUrls(JpsOrderRootType rootType) {
158     List<String> urls = new ArrayList<String>();
159     for (JpsLibraryRoot root : getRoots(rootType)) {
160       switch (root.getInclusionOptions()) {
161         case ROOT_ITSELF:
162           urls.add(root.getUrl());
163           break;
164         case ARCHIVES_UNDER_ROOT:
165           collectArchives(JpsPathUtil.urlToFile(root.getUrl()), false, urls);
166           break;
167         case ARCHIVES_UNDER_ROOT_RECURSIVELY:
168           collectArchives(JpsPathUtil.urlToFile(root.getUrl()), true, urls);
169           break;
170       }
171     }
172     return urls;
173   }
174
175   private static final Set<String> AR_EXTENSIONS  = ContainerUtil.newTroveSet(FileUtil.PATH_HASHING_STRATEGY, "jar", "zip", "swc", "ane");
176
177   private static void collectArchives(File file, boolean recursively, List<String> result) {
178     final File[] children = file.listFiles();
179     if (children != null) {
180       for (File child : children) {
181         final String extension = FileUtilRt.getExtension(child.getName());
182         if (child.isDirectory()) {
183           if (recursively) {
184             collectArchives(child, recursively, result);
185           }
186         }
187         // todo [nik] get list of extensions mapped to Archive file type from IDE settings
188         else if (AR_EXTENSIONS.contains(extension)) {
189           result.add(JpsPathUtil.getLibraryRootUrl(child));
190         }
191       }
192     }
193   }
194 }