1 /*******************************************************************************
2  * Copyright (c) 2000, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Baltasar Belyavsky (Texas Instruments) - [361675] Order mismatch when saving/restoring workspace trees
14  *     Broadcom Corporation - ongoing development
15  *     Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427
16  *******************************************************************************/
17 package org.eclipse.core.internal.resources;
18 
19 import java.io.DataInputStream;
20 import java.io.IOException;
21 import java.util.*;
22 import org.eclipse.core.internal.events.BuilderPersistentInfo;
23 import org.eclipse.core.internal.utils.Messages;
24 import org.eclipse.core.internal.utils.Policy;
25 import org.eclipse.core.internal.watson.ElementTree;
26 import org.eclipse.core.resources.IProject;
27 import org.eclipse.core.resources.IResourceStatus;
28 import org.eclipse.core.runtime.*;
29 
30 /**
31  * Reads version 2 of the workspace tree file format.
32  *
33  * This version differs from version 1 in the amount of information that is persisted
34  * for each builder. Version 1 only stored builder names and trees. Version
35  * 2 stores builder names, project names, trees, and interesting projects for
36  * each builder.
37  * <p>
38  * Since 3.7 support has been added for persisting multiple delta trees for
39  * multi-configuration builders.
40  * </p>
41  * <p>
42  * To achieve backwards compatibility, the new additional information is
43  * appended to the existing workspace tree file.  This allows the workspace
44  * to be opened, and function, with older eclipse products.
45  * </p>
46  */
47 public class WorkspaceTreeReader_2 extends WorkspaceTreeReader_1 {
48 
49 	private List<BuilderPersistentInfo> builderInfos;
50 
WorkspaceTreeReader_2(Workspace workspace)51 	public WorkspaceTreeReader_2(Workspace workspace) {
52 		super(workspace);
53 	}
54 
55 	@Override
getVersion()56 	protected int getVersion() {
57 		return ICoreConstants.WORKSPACE_TREE_VERSION_2;
58 	}
59 
60 	/*
61 	 * overwritten from WorkspaceTreeReader_1
62 	 */
63 	@Override
readBuildersPersistentInfo(IProject project, DataInputStream input, List<BuilderPersistentInfo> builders, IProgressMonitor monitor)64 	protected void readBuildersPersistentInfo(IProject project, DataInputStream input, List<BuilderPersistentInfo> builders, IProgressMonitor monitor) throws IOException {
65 		monitor = Policy.monitorFor(monitor);
66 		try {
67 			int builderCount = input.readInt();
68 			for (int i = 0; i < builderCount; i++) {
69 				BuilderPersistentInfo info = readBuilderInfo(project, input, i);
70 				// read interesting projects
71 				int n = input.readInt();
72 				IProject[] projects = new IProject[n];
73 				for (int j = 0; j < n; j++)
74 					projects[j] = workspace.getRoot().getProject(input.readUTF());
75 				info.setInterestingProjects(projects);
76 				builders.add(info);
77 			}
78 		} finally {
79 			monitor.done();
80 		}
81 	}
82 
83 	/**
84 	 * Read a workspace tree storing information about multiple projects.
85 	 * Overrides {@link WorkspaceTreeReader_1#readTree(DataInputStream, IProgressMonitor)}
86 	 */
87 	@Override
readTree(DataInputStream input, IProgressMonitor monitor)88 	public void readTree(DataInputStream input, IProgressMonitor monitor) throws CoreException {
89 		monitor = Policy.monitorFor(monitor);
90 		String message;
91 		try {
92 			message = Messages.resources_reading;
93 			monitor.beginTask(message, Policy.totalWork);
94 
95 			builderInfos = new ArrayList<>(20);
96 
97 			// Read the version 2 part of the file, but don't set the builder info in
98 			// the projects. Store it in builderInfos instead.
99 			readWorkspaceFields(input, Policy.subMonitorFor(monitor, Policy.opWork * 20 / 100));
100 
101 			HashMap<String, SavedState> savedStates = new HashMap<>(20);
102 			List<SavedState> pluginsToBeLinked = new ArrayList<>(20);
103 			readPluginsSavedStates(input, savedStates, pluginsToBeLinked, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
104 			workspace.getSaveManager().setPluginsSavedState(savedStates);
105 
106 			int treeIndex = pluginsToBeLinked.size();
107 
108 			List<BuilderPersistentInfo> buildersToBeLinked = new ArrayList<>(20);
109 			readBuildersPersistentInfo(null, input, buildersToBeLinked, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
110 
111 			final ElementTree[] trees = readTrees(Path.ROOT, input, Policy.subMonitorFor(monitor, Policy.opWork * 40 / 100));
112 			linkPluginsSavedStateToTrees(pluginsToBeLinked, trees, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
113 			linkBuildersToTrees(buildersToBeLinked, trees, treeIndex, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
114 
115 			// Since 3.7: Read the per-configuration trees if available
116 			if (input.available() > 0) {
117 				treeIndex += buildersToBeLinked.size();
118 
119 				buildersToBeLinked.clear();
120 				readBuildersPersistentInfo(null, input, buildersToBeLinked, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
121 				linkBuildersToTrees(buildersToBeLinked, trees, treeIndex, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
122 
123 				for (BuilderPersistentInfo builderPersistentInfo : builderInfos)
124 					builderPersistentInfo.setConfigName(input.readUTF());
125 			}
126 
127 			// Set the builder infos on the projects
128 			setBuilderInfos(builderInfos);
129 
130 		} catch (IOException e) {
131 			message = Messages.resources_readProjectTree;
132 			throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, message, e);
133 		} finally {
134 			monitor.done();
135 		}
136 	}
137 
138 	/**
139 	 * Read a workspace tree storing information about a single project.
140 	 * Overrides {@link WorkspaceTreeReader_2#readTree(IProject, DataInputStream, IProgressMonitor)}
141 	 */
142 	@Override
readTree(IProject project, DataInputStream input, IProgressMonitor monitor)143 	public void readTree(IProject project, DataInputStream input, IProgressMonitor monitor) throws CoreException {
144 		monitor = Policy.monitorFor(monitor);
145 		String message;
146 		try {
147 			message = Messages.resources_reading;
148 			monitor.beginTask(message, 10);
149 
150 			builderInfos = new ArrayList<>(20);
151 
152 			// Read the version 2 part of the file, but don't set the builder info in
153 			// the projects. It is stored in builderInfos instead.
154 
155 			int treeIndex = 0;
156 
157 			List<BuilderPersistentInfo> buildersToBeLinked = new ArrayList<>(20);
158 			readBuildersPersistentInfo(project, input, buildersToBeLinked, Policy.subMonitorFor(monitor, 1));
159 
160 			ElementTree[] trees = readTrees(project.getFullPath(), input, Policy.subMonitorFor(monitor, 8));
161 			linkBuildersToTrees(buildersToBeLinked, trees, treeIndex, Policy.subMonitorFor(monitor, 1));
162 
163 			// Since 3.7: Read the additional builder information
164 			if (input.available() > 0) {
165 				treeIndex += buildersToBeLinked.size();
166 
167 				List<BuilderPersistentInfo> infos = new ArrayList<>(5);
168 				readBuildersPersistentInfo(project, input, infos, Policy.subMonitorFor(monitor, 1));
169 				linkBuildersToTrees(infos, trees, treeIndex, Policy.subMonitorFor(monitor, 1));
170 
171 				for (BuilderPersistentInfo builderPersistentInfo : builderInfos)
172 					builderPersistentInfo.setConfigName(input.readUTF());
173 			}
174 
175 			// Set the builder info on the projects
176 			setBuilderInfos(builderInfos);
177 
178 		} catch (IOException e) {
179 			message = Messages.resources_readProjectTree;
180 			throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, message, e);
181 		} finally {
182 			monitor.done();
183 		}
184 	}
185 
186 	/**
187 	 * This implementation allows pre-3.7 version 2 and post-3.7 version 2 information to be loaded in separate passes.
188 	 * Links trees with the given builders, but does not add them to the projects.
189 	 * Overrides {@link WorkspaceTreeReader_1#linkBuildersToTrees(List, ElementTree[], int, IProgressMonitor)}
190 	 */
191 	@Override
linkBuildersToTrees(List<BuilderPersistentInfo> buildersToBeLinked, ElementTree[] trees, int index, IProgressMonitor monitor)192 	protected void linkBuildersToTrees(List<BuilderPersistentInfo> buildersToBeLinked, ElementTree[] trees, int index, IProgressMonitor monitor) {
193 		monitor = Policy.monitorFor(monitor);
194 		try {
195 			for (int i = 0; i < buildersToBeLinked.size(); i++) {
196 				BuilderPersistentInfo info = buildersToBeLinked.get(i);
197 				info.setLastBuildTree(trees[index++]);
198 				builderInfos.add(info);
199 			}
200 		} finally {
201 			monitor.done();
202 		}
203 	}
204 
205 	/**
206 	 * Given a list of builder infos, group them by project and set them on the project.
207 	 */
setBuilderInfos(List<BuilderPersistentInfo> infos)208 	private void setBuilderInfos(List<BuilderPersistentInfo> infos) {
209 		Map<String, List<BuilderPersistentInfo>> groupedInfos = new HashMap<>();
210 		for (BuilderPersistentInfo info : infos) {
211 			if (!groupedInfos.containsKey(info.getProjectName()))
212 				groupedInfos.put(info.getProjectName(), new ArrayList<>());
213 			groupedInfos.get(info.getProjectName()).add(info);
214 		}
215 		for (Map.Entry<String, List<BuilderPersistentInfo>> entry : groupedInfos.entrySet()) {
216 			IProject proj = workspace.getRoot().getProject(entry.getKey());
217 			workspace.getBuildManager().setBuildersPersistentInfo(proj, entry.getValue());
218 		}
219 	}
220 
221 }
222