Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: c61d47c11f5d8ef64648a893cdcffc27fa0d13c2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*******************************************************************************
 * Copyright (c) 2008, 2016 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.equinox.internal.p2.persistence;

import java.io.*;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.equinox.internal.p2.core.helpers.OrderedProperties;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.metadata.VersionRange;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.BundleContext;
import org.xml.sax.*;

/*
 * Class used to read a composite repository.
 */
public class CompositeParser extends XMLParser implements XMLConstants {

	private static final Version CURRENT_VERSION = Version.createOSGi(1, 0, 0);
	static final VersionRange XML_TOLERANCE = new VersionRange(CURRENT_VERSION, true, Version.createOSGi(2, 0, 0), false);
	private static final String REQUIRED_CAPABILITY_ELEMENT = "required"; //$NON-NLS-1$
	private static final String REPOSITORY_ELEMENT = "repository"; //$NON-NLS-1$
	String repositoryType;
	private CompositeRepositoryState theState;

	protected class ChildrenHandler extends AbstractHandler {
		private ArrayList<URI> children;

		public ChildrenHandler(AbstractHandler parentHandler, Attributes attributes) {
			super(parentHandler, CHILDREN_ELEMENT);
			String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE);
			children = (size != null ? new ArrayList<>(Integer.parseInt(size)) : new ArrayList<>(4));
		}

		public URI[] getChildren() {
			return children.toArray(new URI[children.size()]);
		}

		@Override
		public void startElement(String name, Attributes attributes) {
			if (name.equals(CHILD_ELEMENT)) {
				new ChildHandler(this, attributes, children);
			} else {
				invalidElement(name, attributes);
			}
		}
	}

	protected class ChildHandler extends AbstractHandler {
		private final String[] required = new String[] {LOCATION_ELEMENT};
		private final String[] optional = new String[] {};

		URI currentRepo = null;

		private List<URI> repos;

		public ChildHandler(AbstractHandler parentHandler, Attributes attributes, List<URI> repos) {
			super(parentHandler, CHILD_ELEMENT);
			String[] values = parseAttributes(attributes, required, optional);
			this.repos = repos;
			//skip entire subrepository if the location is missing
			if (values[0] == null)
				return;
			currentRepo = checkURI(REQUIRED_CAPABILITY_ELEMENT, URI_ATTRIBUTE, values[0]);

		}

		@Override
		public void startElement(String name, Attributes attributes) {
			checkCancel();
		}

		@Override
		protected void finished() {
			if (currentRepo != null)
				repos.add(currentRepo);
		}
	}

	private final class RepositoryDocHandler extends DocHandler {

		public RepositoryDocHandler(String rootName, RootHandler rootHandler) {
			super(rootName, rootHandler);
		}

		@Override
		public void processingInstruction(String target, String data) throws SAXException {
			if (repositoryType.equals(target)) {
				Version repositoryVersion = extractPIVersion(target, data);
				if (!XML_TOLERANCE.isIncluded(repositoryVersion)) {
					throw new SAXException(NLS.bind(Messages.io_IncompatibleVersion, repositoryVersion, XML_TOLERANCE));
				}
			}
		}
	}

	/*
	 * Handler for the "repository" attribute.
	 */
	private final class RepositoryHandler extends RootHandler {

		private final String[] required = new String[] {NAME_ATTRIBUTE, TYPE_ATTRIBUTE, VERSION_ATTRIBUTE};
		private final String[] optional = new String[] {DESCRIPTION_ATTRIBUTE, PROVIDER_ATTRIBUTE};
		private PropertiesHandler propertiesHandler = null;
		private ChildrenHandler childrenHandler = null;
		private CompositeRepositoryState state;
		private String[] attrValues = new String[required.length + optional.length];

		public RepositoryHandler() {
			super();
		}

		public CompositeRepositoryState getRepository() {
			return state;
		}

		@Override
		protected void handleRootAttributes(Attributes attributes) {
			attrValues = parseAttributes(attributes, required, optional);
			attrValues[2] = checkVersion(REPOSITORY_ELEMENT, VERSION_ATTRIBUTE, attrValues[2]).toString();
		}

		@Override
		public void startElement(String name, Attributes attributes) {
			if (name==null) {
				invalidElement(name, attributes);
			} else switch (name) {
				case PROPERTIES_ELEMENT:
					if (propertiesHandler == null) {
						propertiesHandler = new PropertiesHandler(this, attributes);
					} else {
						duplicateElement(this, name, attributes);
					}
					break;
				case CHILDREN_ELEMENT:
					if (childrenHandler == null) {
						childrenHandler = new ChildrenHandler(this, attributes);
					} else {
						duplicateElement(this, name, attributes);
					}
					break;
				default:
					invalidElement(name, attributes);
					break;
			}
		}

		/*
		 * If we parsed valid XML then fill in our repository state object with the parsed data.
		 */
		@Override
		protected void finished() {
			if (isValidXML()) {
				state = new CompositeRepositoryState();
				state.setName(attrValues[0]);
				state.setType(attrValues[1]);
				state.setVersion(attrValues[2]);
				state.setDescription(attrValues[3]);
				state.setProvider(attrValues[4]);
				state.setProperties((propertiesHandler == null ? new OrderedProperties(0) //
						: propertiesHandler.getProperties()));
				state.setChildren((childrenHandler == null ? new URI[0] //
						: childrenHandler.getChildren()));
			}
		}
	}

	public CompositeParser(BundleContext context, String bundleId, String type) {
		super(context, bundleId);
		this.repositoryType = type;
	}

	public void parse(File file) throws IOException {
		// don't overwrite if we already have a filename/location
		if (errorContext == null)
			setErrorContext(file.getAbsolutePath());
		parse(new FileInputStream(file));
	}

	public synchronized void parse(InputStream stream) throws IOException {
		this.status = null;
		try {
			// TODO: currently not caching the parser since we make no assumptions
			//		 or restrictions on concurrent parsing
			getParser();
			RepositoryHandler repositoryHandler = new RepositoryHandler();
			xmlReader.setContentHandler(new RepositoryDocHandler(REPOSITORY_ELEMENT, repositoryHandler));
			xmlReader.parse(new InputSource(stream));
			if (isValidXML()) {
				theState = repositoryHandler.getRepository();
			}
		} catch (SAXException e) {
			IOException ioException = new IOException(e.getMessage());
			ioException.initCause(e);
			throw ioException;
		} catch (ParserConfigurationException e) {
			IOException ioException = new IOException(e.getMessage());
			ioException.initCause(e);
			throw ioException;
		} finally {
			stream.close();
		}
	}

	public CompositeRepositoryState getRepositoryState() {
		return theState;
	}

	//TODO what?
	@Override
	protected Object getRootObject() {
		return null;
	}

	@Override
	protected String getErrorMessage() {
		return Messages.io_parseError;
	}

}

Back to the top