/
DotPatternMapHelpers.java
153 lines (133 loc) · 4.88 KB
/
DotPatternMapHelpers.java
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
/*
* Hibernate OGM, Domain model persistence for NoSQL datastores
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.ogm.datastore.document.impl;
import java.util.Map;
import java.util.regex.Pattern;
import org.hibernate.ogm.datastore.document.association.spi.impl.DocumentHelpers;
import org.hibernate.ogm.datastore.document.options.MapStorageType;
import org.hibernate.ogm.datastore.document.options.spi.MapStorageOption;
import org.hibernate.ogm.dialect.spi.AssociationContext;
import org.hibernate.ogm.model.key.spi.AssociationKey;
/**
* Provides functionality for dealing with (nested) fields of Map documents.
*
* @author Alan Fitton <alan at eth0.org.uk>
* @author Emmanuel Bernard <emmanuel@hibernate.org>
* @author Gunnar Morling
* @author Mark Paluch
*/
public class DotPatternMapHelpers {
private static final Pattern DOT_SEPARATOR_PATTERN = Pattern.compile( "\\." );
/**
* Remove a column from the Map
*
* @param entity the {@link Map} with the column
* @param column the column to remove
*/
public static void resetValue(Map<?, ?> entity, String column) {
// fast path for non-embedded case
if ( !column.contains( "." ) ) {
entity.remove( column );
}
else {
String[] path = DOT_SEPARATOR_PATTERN.split( column );
Object field = entity;
int size = path.length;
for ( int index = 0; index < size; index++ ) {
String node = path[index];
Map parent = (Map) field;
field = parent.get( node );
if ( field == null && index < size - 1 ) {
//TODO clean up the hierarchy of empty containers
// no way to reach the leaf, nothing to do
return;
}
if ( index == size - 1 ) {
parent.remove( node );
}
}
}
}
public static boolean hasField(Map entity, String dotPath) {
return getValueOrNull( entity, dotPath ) != null;
}
public static <T> T getValueOrNull(Map entity, String dotPath, Class<T> type) {
Object value = getValueOrNull( entity, dotPath );
return type.isInstance( value ) ? type.cast( value ) : null;
}
public static Object getValueOrNull(Map entity, String dotPath) {
// fast path for simple properties
if ( !dotPath.contains( "." ) ) {
return entity.get( dotPath );
}
String[] path = DOT_SEPARATOR_PATTERN.split( dotPath );
int size = path.length;
for ( int index = 0; index < size - 1; index++ ) {
Object next = entity.get( path[index] );
if ( next == null || !( next instanceof Map ) ) {
return null;
}
entity = (Map) next;
}
String field = path[size - 1];
return entity.get( field );
}
/**
* Links the two field names into a single left.right field name.
* If the left field is empty, right is returned
*
* @param left one field name
* @param right the other field name
*
* @return left.right or right if left is an empty string
*/
public static String flatten(String left, String right) {
return left == null || left.isEmpty() ? right : left + "." + right;
}
/**
* Whether the rows of the given association should be stored in a hash using the single row key column as key or
* not.
*/
public static boolean organizeAssociationMapByRowKey(
org.hibernate.ogm.model.spi.Association association,
AssociationKey key,
AssociationContext associationContext) {
if ( association.isEmpty() ) {
return false;
}
if ( key.getMetadata().getRowKeyIndexColumnNames().length != 1 ) {
return false;
}
Object valueOfFirstRow = association.get( association.getKeys().iterator().next() )
.get( key.getMetadata().getRowKeyIndexColumnNames()[0] );
if ( !( valueOfFirstRow instanceof String ) ) {
return false;
}
// The list style may be explicitly enforced for compatibility reasons
return getMapStorage( associationContext ) == MapStorageType.BY_KEY;
}
private static MapStorageType getMapStorage(AssociationContext associationContext) {
return associationContext.getAssociationTypeContext().getOptionsContext().getUnique( MapStorageOption.class );
}
/**
* Returns the shared prefix with a trailing dot (.) of the association columns or {@literal null} if there is no shared prefix.
*
* @param associationKey the association key
*
* @return the shared prefix with a trailing dot (.) of the association columns or {@literal null}
*/
public static String getColumnSharedPrefixOfAssociatedEntityLink(AssociationKey associationKey) {
String[] associationKeyColumns = associationKey.getMetadata()
.getAssociatedEntityKeyMetadata()
.getAssociationKeyColumns();
// we used to check that columns are the same (in an ordered fashion)
// but to handle List and Map and store indexes / keys at the same level as the id columns
// this check is removed
String prefix = DocumentHelpers.getColumnSharedPrefix( associationKeyColumns );
return prefix == null ? "" : prefix + ".";
}
}