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.field;
15 
16 import org.eclipse.jdt.internal.core.nd.Nd;
17 import org.eclipse.jdt.internal.core.nd.NdNode;
18 import org.eclipse.jdt.internal.core.nd.db.BTree;
19 import org.eclipse.jdt.internal.core.nd.db.ModificationLog;
20 import org.eclipse.jdt.internal.core.nd.db.ModificationLog.Tag;
21 import org.eclipse.jdt.internal.core.nd.db.Database;
22 import org.eclipse.jdt.internal.core.nd.db.EmptyString;
23 import org.eclipse.jdt.internal.core.nd.db.IString;
24 
25 /**
26  * Represents a search key into a global search index.
27  */
28 public class FieldSearchKey<T> extends BaseField implements IDestructableField {
29 	FieldSearchIndex<?> searchIndex;
30 	private final Tag destructTag;
31 	private final Tag putTag;
32 
FieldSearchKey(FieldSearchIndex<?> searchIndex, String structName, int fieldNumber)33 	private FieldSearchKey(FieldSearchIndex<?> searchIndex, String structName, int fieldNumber) {
34 		if (searchIndex != null) {
35 			if (searchIndex.searchKey != null && searchIndex.searchKey != this) {
36 				throw new IllegalArgumentException(
37 					"Attempted to construct a FieldSearchKey referring to a search index that is " //$NON-NLS-1$
38 					+ "already in use by a different key"); //$NON-NLS-1$
39 			}
40 			searchIndex.searchKey = this;
41 		}
42 		this.searchIndex = searchIndex;
43 		setFieldName("field " + fieldNumber + ", a " + getClass().getSimpleName() //$NON-NLS-1$//$NON-NLS-2$
44 				+ " in struct " + structName); //$NON-NLS-1$
45 		this.putTag = ModificationLog.createTag("Writing " + getFieldName()); //$NON-NLS-1$
46 		this.destructTag = ModificationLog.createTag("Destructing " + getFieldName()); //$NON-NLS-1$
47 	}
48 
49 	/**
50 	 * Creates a search key attribute in the given struct which stores an entry in the given global search index
51 	 */
create(StructDef<B> builder, FieldSearchIndex<B> searchIndex)52 	public static <T, B extends NdNode> FieldSearchKey<T> create(StructDef<B> builder,
53 			FieldSearchIndex<B> searchIndex) {
54 		FieldSearchKey<T> result = new FieldSearchKey<T>(searchIndex, builder.getStructName(), builder.getNumFields());
55 
56 		builder.add(result);
57 		builder.addDestructableField(result);
58 
59 		return result;
60 	}
61 
put(Nd nd, long address, String newString)62 	public void put(Nd nd, long address, String newString) {
63 		put(nd, address, newString.toCharArray());
64 	}
65 
66 	/**
67 	 * Sets the value of the key and inserts it into the index if it is not already present
68 	 */
put(Nd nd, long address, char[] newString)69 	public void put(Nd nd, long address, char[] newString) {
70 		Database db = nd.getDB();
71 		db.getLog().start(this.putTag);
72 		try {
73 			cleanup(nd, address);
74 
75 			BTree btree = this.searchIndex.get(nd, Database.DATA_AREA_OFFSET);
76 			db.putRecPtr(address + this.offset, db.newString(newString).getRecord());
77 			btree.insert(address);
78 		} finally {
79 			db.getLog().end(this.putTag);
80 		}
81 	}
82 
get(Nd nd, long address)83 	public IString get(Nd nd, long address) {
84 		Database db = nd.getDB();
85 		long namerec = db.getRecPtr(address + this.offset);
86 
87 		if (namerec == 0) {
88 			return EmptyString.create();
89 		}
90 		return db.getString(namerec);
91 	}
92 
93 	@Override
destruct(Nd nd, long address)94 	public void destruct(Nd nd, long address) {
95 		Database db = nd.getDB();
96 		db.getLog().start(this.destructTag);
97 		try {
98 			cleanup(nd, address);
99 		} finally {
100 			db.getLog().end(this.destructTag);
101 		}
102 	}
103 
cleanup(Nd nd, long address)104 	private void cleanup(Nd nd, long address) {
105 		boolean isInIndex = isInIndex(nd, address);
106 
107 		if (isInIndex) {
108 			// Remove this entry from the search index
109 			this.searchIndex.get(nd, Database.DATA_AREA_OFFSET).delete(address);
110 
111 			get(nd, address).delete();
112 			nd.getDB().putRecPtr(address + this.offset, 0);
113 		}
114 	}
115 
116 	/**
117 	 * Clears this key and removes it from the search index
118 	 */
removeFromIndex(Nd nd, long address)119 	public void removeFromIndex(Nd nd, long address) {
120 		cleanup(nd, address);
121 	}
122 
123 	/**
124 	 * Returns true iff this key is currently in the index
125 	 */
isInIndex(Nd nd, long address)126 	public boolean isInIndex(Nd nd, long address) {
127 		long fieldAddress = address + this.offset;
128 		Database db = nd.getDB();
129 		long namerec = db.getRecPtr(fieldAddress);
130 
131 		boolean isInIndex = namerec != 0;
132 		return isInIndex;
133 	}
134 
135 	@Override
getRecordSize()136 	public int getRecordSize() {
137 		return FieldString.RECORD_SIZE;
138 	}
139 }
140