1 /*
2 Copyright (c) 2026 James Ahlborn
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 package com.healthmarketscience.jackcess;
18
19 import java.io.IOException;
20 import java.time.LocalDateTime;
21 import java.util.List;
22 import java.util.stream.Stream;
23 import java.util.stream.StreamSupport;
24
25 import com.healthmarketscience.jackcess.query.Query;
26
27 /**
28 * A name map, the data behind the ms access "Name AutoCorrect" option. It
29 * holds a record for each table and column an object depends on, tied to the
30 * guid in the {@value PropertyMap#GUID_PROP} property of that table or
31 * column. When a table or column is renamed, ms access finds the old name
32 * here and changes the places which use it.
33 * <p>
34 * Two kinds of object carry one:
35 * <ul>
36 * <li>a table, in its {@value PropertyMap#NAME_MAP_PROP} property, which
37 * {@link Table#getNameMap} reads as a {@link TableNameMap}</li>
38 * <li>a query, in its {@value PropertyMap#QUERY_NAME_MAP_PROP} property,
39 * which {@link com.healthmarketscience.jackcess.query.Query#getNameMap}
40 * reads. It has a table record for each table or query the query uses
41 * and a column record for each column it uses. It cannot be changed, because
42 * ms access repairs a query when it finds a record whose guid now names an
43 * object with a different name, and a record changed without the sql of
44 * the query would stop that repair</li>
45 * </ul>
46 * Ms access does not require a name map, and nothing in jackcess updates one
47 * when a table changes, so the records can disagree with the database.
48 * {@link #findMismatches} lists where they do.
49 *
50 * @author James Ahlborn
51 * @usage _intermediate_class_
52 */
53 public interface NameMap extends Iterable<NameMap.Record>
54 {
55 /**
56 * @return the table records, in the order of the name map
57 */
58 public List<Record> getTableRecords();
59
60 /**
61 * @return the column records, in the order of the name map
62 */
63 public List<Record> getColumnRecords();
64
65 /**
66 * @return the first table or column record with the given guid, or {@code
67 * null} if there is none
68 */
69 public Record getRecord(String guid);
70
71 /**
72 * @return the tag in the end record, which is a value of the ms access
73 * build which wrote the name map, or {@code null} if the name map
74 * has no end record
75 */
76 public Integer getWriterTag();
77
78 /**
79 * Compares the table and column records with the tables and columns they
80 * name.
81 *
82 * @return every difference, empty if the name map agrees with the database
83 */
84 public List<Mismatch> findMismatches() throws IOException;
85
86 /**
87 * @return a Stream using the default Iterator.
88 */
89 default public Stream<NameMap.Record> stream() {
90 return StreamSupport.stream(spliterator(), false);
91 }
92
93 /**
94 * The kinds of record in a name map.
95 */
96 public enum RecordType
97 {
98 /** a table. A table name map has one, and it is the first record */
99 TABLE,
100 /** a name which an expression in a table uses */
101 REFERENCE,
102 /** a column */
103 COLUMN,
104 /** the end of the name map. Older builds of ms access did not write
105 it */
106 END,
107 /** a type jackcess does not know, which is kept as it was read */
108 UNKNOWN;
109 }
110
111 /**
112 * One record of a name map.
113 */
114 public interface Record
115 {
116 public RecordType getType();
117
118 /**
119 * @return the guid of the object, or {@code null} for a record which
120 * names no object (a reference or the end record)
121 */
122 public String getGuid();
123
124 /**
125 * @return the guid of the table, for a column or a reference record,
126 * otherwise {@code null}
127 */
128 public String getParentGuid();
129
130 public String getName();
131
132 /**
133 * @throws UnsupportedOperationException if the name map belongs to a
134 * query
135 */
136 public void setName(String name);
137
138 /**
139 * @return the date in a table record, otherwise {@code null}. It is the
140 * last modified date of the table when ms access wrote the name
141 * map: when it rewrote the name map of the table, or when it
142 * saved the query.
143 */
144 public LocalDateTime getDate();
145 }
146
147 /**
148 * The kinds of difference between a name map and the database.
149 */
150 public enum MismatchType
151 {
152 /** a column of the table which holds the name map has no record */
153 MISSING_RECORD,
154 /** a record names no table or column, by guid or by name */
155 MISSING_OBJECT,
156 /** the object with the guid of a record has a different name */
157 NAME_DIFFERS,
158 /** the object with the name of a record has a different guid */
159 GUID_DIFFERS;
160 }
161
162 /**
163 * One difference between a name map and the database.
164 */
165 public interface Mismatch
166 {
167 public MismatchType getType();
168
169 /**
170 * @return the table the difference is in, or {@code null} if the record
171 * names a query or no table was found for it
172 */
173 public Table getTable();
174
175 /**
176 * @return the query the difference is in, for a record of a query name
177 * map which names the query it reads, otherwise {@code null}
178 */
179 public Query getQuery();
180
181 /**
182 * @return the column, or {@code null} if the difference is in a table
183 * record or no column was found for the record
184 */
185 public Column getColumn();
186
187 /**
188 * @return the record, or {@code null} if the column has no record
189 */
190 public Record getRecord();
191 }
192 }