| /******************************************************************************* |
| * Copyright (c) 2003, 2012 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Rob Harrop - SpringSource Inc. (bug 247522) |
| *******************************************************************************/ |
| package org.eclipse.osgi.internal.resolver; |
| |
| import java.io.*; |
| import java.util.*; |
| import org.eclipse.osgi.service.resolver.*; |
| import org.eclipse.osgi.service.resolver.VersionRange; |
| import org.osgi.framework.*; |
| |
| /** |
| * This class is <strong>not</strong> thread safe. Instances must not be |
| * shared across multiple threads. |
| */ |
| class StateWriter { |
| |
| // objectTable will be a hashmap of objects. The objects will be things |
| // like BundleDescription, ExportPackageDescription, Version etc.. The integer |
| // index value will be used in the cache to allow cross-references in the |
| // cached state. |
| private final Map<Object, Integer> objectTable = new HashMap<Object, Integer>(); |
| |
| private final List<Object> forcedWrite = new ArrayList<Object>(); |
| |
| private int addToObjectTable(Object object) { |
| Integer cur = objectTable.get(object); |
| if (cur != null) |
| return cur.intValue(); |
| objectTable.put(object, new Integer(objectTable.size())); |
| // return the index of the object just added (i.e. size - 1) |
| return (objectTable.size() - 1); |
| } |
| |
| private int getFromObjectTable(Object object) { |
| if (objectTable != null) { |
| Object objectResult = objectTable.get(object); |
| if (objectResult != null) { |
| return ((Integer) objectResult).intValue(); |
| } |
| } |
| return -1; |
| } |
| |
| private boolean writePrefix(Object object, DataOutputStream out) throws IOException { |
| if (writeIndex(object, out)) |
| return true; |
| // add this object to the object table first |
| int index = addToObjectTable(object); |
| out.writeByte(StateReader.OBJECT); |
| out.writeInt(index); |
| return false; |
| } |
| |
| private void writeStateDeprecated(StateImpl state, DataOutputStream out) throws IOException { |
| out.write(StateReader.STATE_CACHE_VERSION); |
| if (writePrefix(state, out)) |
| return; |
| out.writeLong(state.getTimeStamp()); |
| // write the platform property keys |
| String[] platformPropKeys = state.getPlatformPropertyKeys(); |
| writePlatformProp(platformPropKeys, out); |
| Dictionary<Object, Object>[] propSet = state.getPlatformProperties(); |
| out.writeInt(propSet.length); |
| for (int i = 0; i < propSet.length; i++) { |
| Dictionary<Object, Object> props = propSet[i]; |
| out.writeInt(platformPropKeys.length); |
| for (int j = 0; j < platformPropKeys.length; j++) |
| writePlatformProp(props.get(platformPropKeys[j]), out); |
| } |
| BundleDescription[] bundles = state.getBundles(); |
| StateHelperImpl.getInstance().sortBundles(bundles); |
| out.writeInt(bundles.length); |
| if (bundles.length == 0) |
| return; |
| for (int i = 0; i < bundles.length; i++) |
| writeBundleDescription(bundles[i], out, false); |
| out.writeBoolean(state.isResolved()); |
| // save the lazy data offset |
| out.writeInt(out.size()); |
| for (int i = 0; i < bundles.length; i++) |
| writeBundleDescriptionLazyData(bundles[i], out); |
| } |
| |
| public void saveState(StateImpl state, File stateFile, File lazyFile) throws IOException { |
| DataOutputStream outLazy = null; |
| DataOutputStream outState = null; |
| FileOutputStream fosLazy = null; |
| FileOutputStream fosState = null; |
| synchronized (state.monitor) { |
| try { |
| BundleDescription[] bundles = state.getBundles(); |
| StateHelperImpl.getInstance().sortBundles(bundles); |
| // need to prime the object table with all bundles |
| // this allows us to write only indexes to bundles in the lazy data |
| for (int i = 0; i < bundles.length; i++) { |
| addToObjectTable(bundles[i]); |
| if (bundles[i].getHost() != null) |
| addToObjectTable(bundles[i].getHost()); |
| } |
| // first write the lazy data to get the offsets and sizes to the lazy data |
| fosLazy = new FileOutputStream(lazyFile); |
| outLazy = new DataOutputStream(new BufferedOutputStream(fosLazy)); |
| for (int i = 0; i < bundles.length; i++) |
| writeBundleDescriptionLazyData(bundles[i], outLazy); |
| // now write the state data |
| fosState = new FileOutputStream(stateFile); |
| outState = new DataOutputStream(new BufferedOutputStream(fosState)); |
| outState.write(StateReader.STATE_CACHE_VERSION); |
| if (writePrefix(state, outState)) |
| return; |
| outState.writeLong(state.getTimeStamp()); |
| // write the platform property keys |
| String[] platformPropKeys = state.getPlatformPropertyKeys(); |
| writePlatformProp(platformPropKeys, outState); |
| // write the platform property values |
| Dictionary<Object, Object>[] propSet = state.getPlatformProperties(); |
| outState.writeInt(propSet.length); |
| for (int i = 0; i < propSet.length; i++) { |
| Dictionary<Object, Object> props = propSet[i]; |
| outState.writeInt(platformPropKeys.length); |
| for (int j = 0; j < platformPropKeys.length; j++) |
| writePlatformProp(props.get(platformPropKeys[j]), outState); |
| } |
| outState.writeInt(bundles.length); |
| for (int i = 0; i < bundles.length; i++) |
| // write out each bundle with the force flag set to make sure |
| // the data is written at least once in the non-lazy state data |
| writeBundleDescription(bundles[i], outState, true); |
| // write the DisabledInfos |
| DisabledInfo[] infos = state.getDisabledInfos(); |
| outState.writeInt(infos.length); |
| for (int i = 0; i < infos.length; i++) |
| writeDisabledInfo(infos[i], outState); |
| outState.writeBoolean(state.isResolved()); |
| } finally { |
| if (outLazy != null) { |
| try { |
| outLazy.flush(); |
| fosLazy.getFD().sync(); |
| } catch (IOException e) { |
| // do nothing, we tried |
| } |
| try { |
| outLazy.close(); |
| } catch (IOException e) { |
| // do nothing |
| } |
| } |
| if (outState != null) { |
| try { |
| outState.flush(); |
| fosState.getFD().sync(); |
| } catch (IOException e) { |
| // do nothing, we tried |
| } |
| try { |
| outState.close(); |
| } catch (IOException e) { |
| // do nothing |
| } |
| } |
| } |
| } |
| } |
| |
| private void writePlatformProp(Object obj, DataOutputStream out) throws IOException { |
| if (!(obj instanceof String) && !(obj instanceof String[])) |
| out.writeByte(StateReader.NULL); |
| else { |
| out.writeByte(StateReader.OBJECT); |
| if (obj instanceof String) { |
| out.writeInt(1); |
| writeStringOrNull((String) obj, out); |
| } else { |
| String[] props = (String[]) obj; |
| out.writeInt(props.length); |
| for (int i = 0; i < props.length; i++) |
| writeStringOrNull(props[i], out); |
| } |
| } |
| } |
| |
| /* |
| * The force flag is used when writing the non-lazy state data. This forces the data to be |
| * written once even if the object exists in the object table. |
| * This is needed because we want to write the lazy data first but we only want |
| * to include indexes to the actual bundles in the lazy data. To do this we |
| * prime the object table with all the bundles first. Then we write the |
| * lazy data. Finally we write the non-lazy data and force a write of the |
| * bundles data once even if the bundle is in the object table. |
| */ |
| private void writeBundleDescription(BundleDescription bundle, DataOutputStream out, boolean force) throws IOException { |
| if (force && !forcedWrite.contains(bundle)) { |
| int index = addToObjectTable(bundle); |
| out.writeByte(StateReader.OBJECT); |
| out.writeInt(index); |
| forcedWrite.add(bundle); |
| } else if (writePrefix(bundle, out)) |
| return; |
| // first write out non-lazy loaded data |
| out.writeLong(bundle.getBundleId()); // ID must be the first thing |
| writeBaseDescription(bundle, out); |
| out.writeInt(((BundleDescriptionImpl) bundle).getLazyDataOffset()); |
| out.writeInt(((BundleDescriptionImpl) bundle).getLazyDataSize()); |
| out.writeBoolean(bundle.isResolved()); |
| out.writeBoolean(bundle.isSingleton()); |
| out.writeBoolean(bundle.hasDynamicImports()); |
| out.writeBoolean(bundle.attachFragments()); |
| out.writeBoolean(bundle.dynamicFragments()); |
| writeList(out, (String[]) ((BundleDescriptionImpl) bundle).getDirective(Constants.MANDATORY_DIRECTIVE)); |
| writeMap(out, bundle.getAttributes()); |
| writeMap(out, ((BundleDescriptionImpl) bundle).getArbitraryDirectives()); |
| writeHostSpec((HostSpecificationImpl) bundle.getHost(), out, force); |
| |
| List<BundleDescription> dependencies = ((BundleDescriptionImpl) bundle).getBundleDependencies(); |
| out.writeInt(dependencies.size()); |
| for (Iterator<BundleDescription> iter = dependencies.iterator(); iter.hasNext();) |
| writeBundleDescription(iter.next(), out, force); |
| // the rest is lazy loaded data |
| } |
| |
| private void writeBundleDescriptionLazyData(BundleDescription bundle, DataOutputStream out) throws IOException { |
| int dataStart = out.size(); // save the offset of lazy data start |
| int index = getFromObjectTable(bundle); |
| ((BundleDescriptionImpl) bundle).setLazyDataOffset(out.size()); |
| out.writeInt(index); |
| |
| writeStringOrNull(bundle.getLocation(), out); |
| writeStringOrNull(bundle.getPlatformFilter(), out); |
| |
| ExportPackageDescription[] exports = bundle.getExportPackages(); |
| out.writeInt(exports.length); |
| for (int i = 0; i < exports.length; i++) |
| writeExportPackageDesc((ExportPackageDescriptionImpl) exports[i], out); |
| |
| ImportPackageSpecification[] imports = bundle.getImportPackages(); |
| out.writeInt(imports.length); |
| for (int i = 0; i < imports.length; i++) |
| writeImportPackageSpec((ImportPackageSpecificationImpl) imports[i], out); |
| |
| BundleSpecification[] requiredBundles = bundle.getRequiredBundles(); |
| out.writeInt(requiredBundles.length); |
| for (int i = 0; i < requiredBundles.length; i++) |
| writeBundleSpec((BundleSpecificationImpl) requiredBundles[i], out); |
| |
| ExportPackageDescription[] selectedExports = bundle.getSelectedExports(); |
| if (selectedExports == null) { |
| out.writeInt(0); |
| } else { |
| out.writeInt(selectedExports.length); |
| for (int i = 0; i < selectedExports.length; i++) |
| writeExportPackageDesc((ExportPackageDescriptionImpl) selectedExports[i], out); |
| } |
| |
| ExportPackageDescription[] substitutedExports = bundle.getSubstitutedExports(); |
| if (substitutedExports == null) { |
| out.writeInt(0); |
| } else { |
| out.writeInt(substitutedExports.length); |
| for (int i = 0; i < substitutedExports.length; i++) |
| writeExportPackageDesc((ExportPackageDescriptionImpl) substitutedExports[i], out); |
| } |
| |
| ExportPackageDescription[] resolvedImports = bundle.getResolvedImports(); |
| if (resolvedImports == null) { |
| out.writeInt(0); |
| } else { |
| out.writeInt(resolvedImports.length); |
| for (int i = 0; i < resolvedImports.length; i++) |
| writeExportPackageDesc((ExportPackageDescriptionImpl) resolvedImports[i], out); |
| } |
| |
| BundleDescription[] resolvedRequires = bundle.getResolvedRequires(); |
| if (resolvedRequires == null) { |
| out.writeInt(0); |
| } else { |
| out.writeInt(resolvedRequires.length); |
| for (int i = 0; i < resolvedRequires.length; i++) |
| writeBundleDescription(resolvedRequires[i], out, false); |
| } |
| |
| String[] ees = bundle.getExecutionEnvironments(); |
| out.writeInt(ees.length); |
| for (int i = 0; i < ees.length; i++) |
| writeStringOrNull(ees[i], out); |
| |
| Map<String, Long> dynamicStamps = ((BundleDescriptionImpl) bundle).getDynamicStamps(); |
| if (dynamicStamps == null) |
| out.writeInt(0); |
| else { |
| out.writeInt(dynamicStamps.size()); |
| for (Iterator<String> pkgs = dynamicStamps.keySet().iterator(); pkgs.hasNext();) { |
| String pkg = pkgs.next(); |
| writeStringOrNull(pkg, out); |
| out.writeLong(dynamicStamps.get(pkg).longValue()); |
| } |
| } |
| |
| GenericDescription[] genericCapabilities = bundle.getGenericCapabilities(); |
| if (genericCapabilities == null) |
| out.writeInt(0); |
| else { |
| out.writeInt(genericCapabilities.length); |
| for (int i = 0; i < genericCapabilities.length; i++) |
| writeGenericDescription(genericCapabilities[i], out); |
| } |
| |
| GenericSpecification[] genericRequires = bundle.getGenericRequires(); |
| if (genericRequires == null) |
| out.writeInt(0); |
| else { |
| out.writeInt(genericRequires.length); |
| for (int i = 0; i < genericRequires.length; i++) |
| writeGenericSpecification((GenericSpecificationImpl) genericRequires[i], out); |
| } |
| |
| GenericDescription[] selectedCapabilities = bundle.getSelectedGenericCapabilities(); |
| if (selectedCapabilities == null) |
| out.writeInt(0); |
| else { |
| out.writeInt(selectedCapabilities.length); |
| for (int i = 0; i < selectedCapabilities.length; i++) |
| writeGenericDescription(selectedCapabilities[i], out); |
| } |
| |
| GenericDescription[] resolvedCapabilities = bundle.getResolvedGenericRequires(); |
| if (resolvedCapabilities == null) |
| out.writeInt(0); |
| else { |
| out.writeInt(resolvedCapabilities.length); |
| for (int i = 0; i < resolvedCapabilities.length; i++) |
| writeGenericDescription(resolvedCapabilities[i], out); |
| } |
| |
| writeNativeCode(bundle.getNativeCodeSpecification(), out); |
| |
| writeMap(out, ((BundleDescriptionImpl) bundle).getWiresInternal()); |
| // save the size of the lazy data |
| ((BundleDescriptionImpl) bundle).setLazyDataSize(out.size() - dataStart); |
| } |
| |
| private void writeDisabledInfo(DisabledInfo disabledInfo, DataOutputStream out) throws IOException { |
| writeStringOrNull(disabledInfo.getPolicyName(), out); |
| writeStringOrNull(disabledInfo.getMessage(), out); |
| writeBundleDescription(disabledInfo.getBundle(), out, false); |
| } |
| |
| private void writeBundleSpec(BundleSpecificationImpl bundle, DataOutputStream out) throws IOException { |
| if (writePrefix(bundle, out)) |
| return; |
| writeVersionConstraint(bundle, out); |
| writeBundleDescription((BundleDescription) bundle.getSupplier(), out, false); |
| out.writeBoolean(bundle.isExported()); |
| out.writeBoolean(bundle.isOptional()); |
| writeMap(out, bundle.getAttributes()); |
| writeMap(out, bundle.getArbitraryDirectives()); |
| } |
| |
| private void writeExportPackageDesc(ExportPackageDescriptionImpl exportPackageDesc, DataOutputStream out) throws IOException { |
| if (writePrefix(exportPackageDesc, out)) |
| return; |
| writeBaseDescription(exportPackageDesc, out); |
| writeBundleDescription(exportPackageDesc.getExporter(), out, false); |
| writeMap(out, exportPackageDesc.getAttributes()); |
| writeMap(out, exportPackageDesc.getDirectives()); |
| writeMap(out, exportPackageDesc.getArbitraryDirectives()); |
| writeExportPackageDesc((ExportPackageDescriptionImpl) exportPackageDesc.getFragmentDeclaration(), out); |
| } |
| |
| private void writeGenericDescription(GenericDescription description, DataOutputStream out) throws IOException { |
| if (writePrefix(description, out)) |
| return; |
| writeBaseDescription(description, out); |
| writeBundleDescription(description.getSupplier(), out, false); |
| writeStringOrNull(description.getType() == GenericDescription.DEFAULT_TYPE ? null : description.getType(), out); |
| Dictionary<String, Object> attrs = description.getAttributes(); |
| Map<String, Object> mapAttrs = new HashMap<String, Object>(attrs.size()); |
| for (Enumeration<String> keys = attrs.keys(); keys.hasMoreElements();) { |
| String key = keys.nextElement(); |
| mapAttrs.put(key, attrs.get(key)); |
| } |
| writeMap(out, mapAttrs); |
| Map<String, String> directives = description.getDeclaredDirectives(); |
| writeMap(out, directives); |
| writeGenericDescription((GenericDescription) ((BaseDescriptionImpl) description).getFragmentDeclaration(), out); |
| } |
| |
| private void writeGenericSpecification(GenericSpecificationImpl specification, DataOutputStream out) throws IOException { |
| if (writePrefix(specification, out)) |
| return; |
| writeVersionConstraint(specification, out); |
| writeStringOrNull(specification.getType() == GenericDescription.DEFAULT_TYPE ? null : specification.getType(), out); |
| GenericDescription[] suppliers = specification.getSuppliers(); |
| out.writeInt(suppliers == null ? 0 : suppliers.length); |
| if (suppliers != null) |
| for (int i = 0; i < suppliers.length; i++) |
| writeGenericDescription(suppliers[i], out); |
| out.writeInt(specification.getResolution()); |
| writeStringOrNull(specification.getMatchingFilter(), out); |
| writeMap(out, specification.getAttributes()); |
| writeMap(out, specification.getArbitraryDirectives()); |
| } |
| |
| private void writeNativeCode(NativeCodeSpecification nativeCodeSpecification, DataOutputStream out) throws IOException { |
| if (nativeCodeSpecification == null) { |
| out.writeBoolean(false); |
| return; |
| } |
| out.writeBoolean(true); |
| out.writeBoolean(nativeCodeSpecification.isOptional()); |
| NativeCodeDescription[] nativeDescs = nativeCodeSpecification.getPossibleSuppliers(); |
| int numDescs = nativeDescs == null ? 0 : nativeDescs.length; |
| out.writeInt(numDescs); |
| int supplierIndex = -1; |
| for (int i = 0; i < numDescs; i++) { |
| if (nativeDescs[i] == nativeCodeSpecification.getSupplier()) |
| supplierIndex = i; |
| writeNativeCodeDescription(nativeDescs[i], out); |
| } |
| out.writeInt(supplierIndex); |
| } |
| |
| private void writeNativeCodeDescription(NativeCodeDescription nativeCodeDescription, DataOutputStream out) throws IOException { |
| writeBaseDescription(nativeCodeDescription, out); |
| writeBundleDescription(nativeCodeDescription.getSupplier(), out, false); |
| Filter filter = nativeCodeDescription.getFilter(); |
| writeStringOrNull(filter == null ? null : filter.toString(), out); |
| writeStringArray(nativeCodeDescription.getLanguages(), out); |
| writeStringArray(nativeCodeDescription.getNativePaths(), out); |
| writeStringArray(nativeCodeDescription.getOSNames(), out); |
| writeVersionRanges(nativeCodeDescription.getOSVersions(), out); |
| writeStringArray(nativeCodeDescription.getProcessors(), out); |
| out.writeBoolean(nativeCodeDescription.hasInvalidNativePaths()); |
| } |
| |
| private void writeVersionRanges(VersionRange[] ranges, DataOutputStream out) throws IOException { |
| out.writeInt(ranges == null ? 0 : ranges.length); |
| if (ranges == null) |
| return; |
| for (int i = 0; i < ranges.length; i++) |
| writeVersionRange(ranges[i], out); |
| } |
| |
| private void writeStringArray(String[] strings, DataOutputStream out) throws IOException { |
| out.writeInt(strings == null ? 0 : strings.length); |
| if (strings == null) |
| return; |
| for (int i = 0; i < strings.length; i++) |
| writeStringOrNull(strings[i], out); |
| } |
| |
| private void writeMap(DataOutputStream out, Map<String, ?> source) throws IOException { |
| if (source == null) { |
| out.writeInt(0); |
| } else { |
| out.writeInt(source.size()); |
| Iterator<String> iter = source.keySet().iterator(); |
| while (iter.hasNext()) { |
| String key = iter.next(); |
| Object value = source.get(key); |
| writeStringOrNull(key, out); |
| if (value instanceof String) { |
| out.writeByte(0); |
| writeStringOrNull((String) value, out); |
| } else if (value instanceof String[]) { |
| out.writeByte(1); |
| writeList(out, (String[]) value); |
| } else if (value instanceof Boolean) { |
| out.writeByte(2); |
| out.writeBoolean(((Boolean) value).booleanValue()); |
| } else if (value instanceof Integer) { |
| out.writeByte(3); |
| out.writeInt(((Integer) value).intValue()); |
| } else if (value instanceof Long) { |
| out.writeByte(4); |
| out.writeLong(((Long) value).longValue()); |
| } else if (value instanceof Double) { |
| out.writeByte(5); |
| out.writeDouble(((Double) value).doubleValue()); |
| } else if (value instanceof Version) { |
| out.writeByte(6); |
| writeVersion((Version) value, out); |
| } else if ("java.net.URI".equals(value.getClass().getName())) { //$NON-NLS-1$ |
| out.writeByte(7); |
| writeStringOrNull(value.toString(), out); |
| } else if (value instanceof List) { |
| writeList(out, (List<?>) value); |
| } |
| } |
| } |
| } |
| |
| private void writeList(DataOutputStream out, List<?> list) throws IOException { |
| byte type = getListType(list); |
| if (type == -2) |
| return; // don't understand the list type |
| out.writeByte(8); |
| out.writeByte(type); |
| out.writeInt(list.size()); |
| for (Object value : list) { |
| switch (type) { |
| case 0 : |
| writeStringOrNull((String) value, out); |
| break; |
| case 3 : |
| out.writeInt(((Integer) value).intValue()); |
| break; |
| case 4 : |
| out.writeLong(((Long) value).longValue()); |
| break; |
| case 5 : |
| out.writeDouble(((Double) value).doubleValue()); |
| break; |
| case 6 : |
| writeVersion((Version) value, out); |
| break; |
| case 7 : |
| writeStateWire((StateWire) value, out); |
| default : |
| break; |
| } |
| } |
| } |
| |
| private void writeStateWire(StateWire wire, DataOutputStream out) throws IOException { |
| VersionConstraint requirement = wire.getDeclaredRequirement(); |
| if (requirement instanceof ImportPackageSpecificationImpl) { |
| out.writeByte(0); |
| writeImportPackageSpec((ImportPackageSpecificationImpl) requirement, out); |
| } else if (requirement instanceof BundleSpecificationImpl) { |
| out.writeByte(1); |
| writeBundleSpec((BundleSpecificationImpl) requirement, out); |
| } else if (requirement instanceof HostSpecificationImpl) { |
| out.writeByte(2); |
| writeHostSpec((HostSpecificationImpl) requirement, out, false); |
| } else if (requirement instanceof GenericSpecificationImpl) { |
| out.writeByte(3); |
| writeGenericSpecification((GenericSpecificationImpl) requirement, out); |
| } else |
| throw new IllegalArgumentException("Unknown requiement type: " + requirement.getClass()); |
| |
| BaseDescription capability = wire.getDeclaredCapability(); |
| if (capability instanceof BundleDescription) |
| writeBundleDescription((BundleDescription) capability, out, false); |
| else if (capability instanceof ExportPackageDescriptionImpl) |
| writeExportPackageDesc((ExportPackageDescriptionImpl) capability, out); |
| else if (capability instanceof GenericDescription) |
| writeGenericDescription((GenericDescription) capability, out); |
| else |
| throw new IllegalArgumentException("Unknown capability type: " + requirement.getClass()); |
| |
| writeBundleDescription(wire.getRequirementHost(), out, false); |
| writeBundleDescription(wire.getCapabilityHost(), out, false); |
| } |
| |
| private byte getListType(List<?> list) { |
| if (list.size() == 0) |
| return -1; |
| Object type = list.get(0); |
| if (type instanceof String) |
| return 0; |
| if (type instanceof Integer) |
| return 3; |
| if (type instanceof Long) |
| return 4; |
| if (type instanceof Double) |
| return 5; |
| if (type instanceof Version) |
| return 6; |
| if (type instanceof StateWire) |
| return 7; |
| return -2; |
| } |
| |
| private void writeList(DataOutputStream out, String[] list) throws IOException { |
| if (list == null) { |
| out.writeInt(0); |
| } else { |
| out.writeInt(list.length); |
| for (int i = 0; i < list.length; i++) |
| writeStringOrNull(list[i], out); |
| } |
| } |
| |
| private void writeBaseDescription(BaseDescription rootDesc, DataOutputStream out) throws IOException { |
| writeStringOrNull(rootDesc.getName(), out); |
| writeVersion(rootDesc.getVersion(), out); |
| } |
| |
| private void writeImportPackageSpec(ImportPackageSpecificationImpl importPackageSpec, DataOutputStream out) throws IOException { |
| if (writePrefix(importPackageSpec, out)) |
| return; |
| writeVersionConstraint(importPackageSpec, out); |
| // TODO this is a hack until the state dynamic loading is cleaned up |
| // we should only write the supplier if we are resolved |
| if (importPackageSpec.getBundle().isResolved()) |
| writeExportPackageDesc((ExportPackageDescriptionImpl) importPackageSpec.getSupplier(), out); |
| else |
| out.writeByte(StateReader.NULL); |
| |
| writeStringOrNull(importPackageSpec.getBundleSymbolicName(), out); |
| writeVersionRange(importPackageSpec.getBundleVersionRange(), out); |
| writeMap(out, importPackageSpec.getAttributes()); |
| writeMap(out, importPackageSpec.getDirectives()); |
| writeMap(out, importPackageSpec.getArbitraryDirectives()); |
| } |
| |
| private void writeHostSpec(HostSpecificationImpl host, DataOutputStream out, boolean force) throws IOException { |
| if (host != null && force && !forcedWrite.contains(host)) { |
| int index = addToObjectTable(host); |
| out.writeByte(StateReader.OBJECT); |
| out.writeInt(index); |
| forcedWrite.add(host); |
| } else if (writePrefix(host, out)) |
| return; |
| writeVersionConstraint(host, out); |
| BundleDescription[] hosts = host.getHosts(); |
| if (hosts == null) { |
| out.writeInt(0); |
| return; |
| } |
| out.writeInt(hosts.length); |
| for (int i = 0; i < hosts.length; i++) |
| writeBundleDescription(hosts[i], out, force); |
| writeMap(out, host.getAttributes()); |
| writeMap(out, host.getArbitraryDirectives()); |
| } |
| |
| // called by writers for VersionConstraintImpl subclasses |
| private void writeVersionConstraint(VersionConstraint constraint, DataOutputStream out) throws IOException { |
| writeStringOrNull(constraint.getName(), out); |
| writeVersionRange(constraint.getVersionRange(), out); |
| } |
| |
| private void writeVersion(Version version, DataOutputStream out) throws IOException { |
| if (version == null || version.equals(Version.emptyVersion)) { |
| out.writeByte(StateReader.NULL); |
| return; |
| } |
| out.writeByte(StateReader.OBJECT); |
| out.writeInt(version.getMajor()); |
| out.writeInt(version.getMinor()); |
| out.writeInt(version.getMicro()); |
| writeQualifier(version.getQualifier(), out); |
| } |
| |
| private void writeVersionRange(VersionRange versionRange, DataOutputStream out) throws IOException { |
| if (versionRange == null || versionRange.equals(VersionRange.emptyRange)) { |
| out.writeByte(StateReader.NULL); |
| return; |
| } |
| out.writeByte(StateReader.OBJECT); |
| writeVersion(versionRange.getMinimum(), out); |
| out.writeBoolean(versionRange.getIncludeMinimum()); |
| writeVersion(versionRange.getMaximum(), out); |
| out.writeBoolean(versionRange.getIncludeMaximum()); |
| } |
| |
| private boolean writeIndex(Object object, DataOutputStream out) throws IOException { |
| if (object == null) { |
| out.writeByte(StateReader.NULL); |
| return true; |
| } |
| int index = getFromObjectTable(object); |
| if (index == -1) |
| return false; |
| out.writeByte(StateReader.INDEX); |
| out.writeInt(index); |
| return true; |
| } |
| |
| public void saveStateDeprecated(StateImpl state, DataOutputStream output) throws IOException { |
| try { |
| writeStateDeprecated(state, output); |
| } finally { |
| output.close(); |
| } |
| } |
| |
| private void writeStringOrNull(String string, DataOutputStream out) throws IOException { |
| if (string == null) |
| out.writeByte(StateReader.NULL); |
| else { |
| byte[] data = string.getBytes(StateReader.UTF_8); |
| |
| if (data.length > 65535) { |
| out.writeByte(StateReader.LONG_STRING); |
| out.writeInt(data.length); |
| out.write(data); |
| } else { |
| out.writeByte(StateReader.OBJECT); |
| out.writeUTF(string); |
| } |
| } |
| } |
| |
| private void writeQualifier(String string, DataOutputStream out) throws IOException { |
| if (string != null && string.length() == 0) |
| string = null; |
| writeStringOrNull(string, out); |
| } |
| } |