1 /*******************************************************************************
2  * Copyright (c) 2015, 2016 Google, Inc 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  *   Stefan Xenos (Google) - Initial implementation
13  *******************************************************************************/
14 package org.eclipse.jdt.internal.core.nd.java;
15 
16 import java.util.HashMap;
17 import java.util.Map;
18 
19 import org.eclipse.jdt.internal.core.nd.Nd;
20 import org.eclipse.jdt.internal.core.nd.db.Database;
21 import org.eclipse.jdt.internal.core.nd.db.IndexException;
22 
23 public abstract class TagTreeReader {
24 	public static final int[] UNUSED_RESULT = new int[1];
25 
26 	public static abstract class TagHandler<T> {
read(Nd nd, long address, TagTreeReader reader, int[] bytesRead)27 		abstract public T read(Nd nd, long address, TagTreeReader reader, int[] bytesRead);
write(Nd nd, long address, TagTreeReader reader, T toWrite, int[] bytesWritten)28 		abstract public void write(Nd nd, long address, TagTreeReader reader, T toWrite, int[] bytesWritten);
getSize(Nd nd, T object, TagTreeReader reader)29 		abstract public int getSize(Nd nd, T object, TagTreeReader reader);
destruct(Nd nd, long address, TagTreeReader reader)30 		public void destruct(Nd nd, long address, TagTreeReader reader) {
31 			// Nothing to do by default
32 		}
33 	}
34 
35 	public static abstract class FixedSizeTagHandler<T> extends TagHandler<T> {
read(Nd nd, long address)36 		protected abstract T read(Nd nd, long address);
write(Nd nd, long address, T value)37 		protected abstract void write(Nd nd, long address, T value);
getSize()38 		protected abstract int getSize();
destruct(Nd nd, long address)39 		protected void destruct(Nd nd, long address) {
40 			// Nothing to do by default
41 		}
42 
43 		@Override
read(Nd nd, long address, TagTreeReader reader, int[] bytesRead)44 		public final T read(Nd nd, long address, TagTreeReader reader, int[] bytesRead) {
45 			bytesRead[0] = getSize();
46 			return read(nd, address);
47 		}
48 
49 		@Override
write(Nd nd, long address, TagTreeReader reader, T value, int[] bytesWritten)50 		public final void write(Nd nd, long address, TagTreeReader reader, T value, int[] bytesWritten) {
51 			bytesWritten[0] = getSize();
52 			write(nd, address, value);
53 		}
54 
55 		@Override
getSize(Nd nd, T object, TagTreeReader reader)56 		public final int getSize(Nd nd, T object, TagTreeReader reader) {
57 			return getSize();
58 		}
59 
60 		@Override
destruct(Nd nd, long address, TagTreeReader reader)61 		public final void destruct(Nd nd, long address, TagTreeReader reader) {
62 			destruct(nd, address);
63 		}
64 	}
65 
66 	private TagHandler<?> readers[] = new TagHandler[256];
67 	private Map<TagHandler<?>, Integer> values = new HashMap<>();
68 
add(byte key, TagHandler<?> reader)69 	public final void add(byte key, TagHandler<?> reader) {
70 		this.readers[key] = reader;
71 		this.values.put(reader, (int) key);
72 	}
73 
read(Nd nd, long address)74 	public final Object read(Nd nd, long address) {
75 		return read(nd, address, UNUSED_RESULT);
76 	}
77 
read(Nd nd, long address, int[] bytesRead)78 	public final Object read(Nd nd, long address, int[] bytesRead) {
79 		long readAddress = address;
80 		Database db = nd.getDB();
81 		byte nextByte = db.getByte(address);
82 		readAddress += Database.BYTE_SIZE;
83 		TagHandler<?> reader = this.readers[nextByte];
84 		if (reader == null) {
85 			throw nd.describeProblem()
86 				.addProblemAddress("tag", address, 1) //$NON-NLS-1$
87 				.build("Found unknown tag with value " + nextByte + " at address " + address); //$NON-NLS-1$//$NON-NLS-2$
88 		}
89 
90 		return reader.read(nd, readAddress, this, bytesRead);
91 	}
92 
getKeyFor(Object toWrite)93 	protected abstract byte getKeyFor(Object toWrite);
94 
write(Nd nd, long address, Object toWrite)95 	public final void write(Nd nd, long address, Object toWrite) {
96 		write(nd, address, toWrite, UNUSED_RESULT);
97 	}
98 
99 	@SuppressWarnings("unchecked")
write(Nd nd, long address, Object toWrite, int[] bytesWritten)100 	public final void write(Nd nd, long address, Object toWrite, int[] bytesWritten) {
101 		byte key = getKeyFor(toWrite);
102 
103 		@SuppressWarnings("rawtypes")
104 		TagHandler handler = this.readers[key];
105 
106 		if (handler == null) {
107 			throw nd.describeProblem().build("Invalid key " + key + " returned from getKeyFor(...)"); //$NON-NLS-1$//$NON-NLS-2$
108 		}
109 
110 		handler.write(nd, address, this, toWrite, bytesWritten);
111 	}
112 
destruct(Nd nd, long address)113 	public final void destruct(Nd nd, long address) {
114 		Database db = nd.getDB();
115 		long readAddress = address;
116 		byte nextByte = db.getByte(readAddress);
117 		readAddress += Database.BYTE_SIZE;
118 
119 		TagHandler<?> handler = this.readers[nextByte];
120 		if (handler == null) {
121 			throw nd.describeProblem()
122 				.addProblemAddress("tag", address, 1) //$NON-NLS-1$
123 				.build("Found unknown tag with value " + nextByte + " at address " + address); //$NON-NLS-1$//$NON-NLS-2$
124 		}
125 
126 		handler.destruct(nd, readAddress, this);
127 	}
128 
129 	@SuppressWarnings("unchecked")
getSize(Nd nd, Object toMeasure)130 	public final int getSize(Nd nd, Object toMeasure) {
131 		byte key = getKeyFor(toMeasure);
132 
133 		@SuppressWarnings("rawtypes")
134 		TagHandler handler = this.readers[key];
135 		if (handler == null) {
136 			throw new IndexException("Attempted to get size of object " + toMeasure.toString() + " with unknown key " //$NON-NLS-1$//$NON-NLS-2$
137 					+ key);
138 		}
139 
140 		return handler.getSize(nd, toMeasure, this);
141 	}
142 }
143