16
16
package ghidra .program .database .data ;
17
17
18
18
import java .io .IOException ;
19
- import java .util .List ;
20
- import java .util .Map ;
19
+ import java .util .*;
21
20
import java .util .concurrent .ConcurrentHashMap ;
22
21
23
22
import db .Record ;
26
25
import ghidra .program .model .data .*;
27
26
import ghidra .program .model .data .DataTypeConflictHandler .ConflictResult ;
28
27
import ghidra .util .InvalidNameException ;
28
+ import ghidra .util .Lock ;
29
29
import ghidra .util .exception .AssertException ;
30
30
import ghidra .util .exception .DuplicateNameException ;
31
31
import ghidra .util .task .TaskMonitor ;
@@ -41,6 +41,7 @@ class CategoryDB extends DatabaseObject implements Category {
41
41
42
42
private LazyLoadingCachingMap <String , CategoryDB > subcategoryMap ;
43
43
private LazyLoadingCachingMap <String , DataType > dataTypeMap ;
44
+ private ConflictMap conflictMap ;
44
45
45
46
/**
46
47
* Category Constructor
@@ -57,19 +58,19 @@ class CategoryDB extends DatabaseObject implements Category {
57
58
this .name = name ;
58
59
this .parent = parent ;
59
60
60
- subcategoryMap = new LazyLoadingCachingMap <>(mgr .lock , CategoryDB . class ) {
61
+ subcategoryMap = new LazyLoadingCachingMap <>(mgr .lock ) {
61
62
@ Override
62
63
public Map <String , CategoryDB > loadMap () {
63
64
return buildSubcategoryMap ();
64
65
}
65
66
};
66
- dataTypeMap = new LazyLoadingCachingMap <>(mgr .lock , DataType . class ) {
67
+ dataTypeMap = new LazyLoadingCachingMap <>(mgr .lock ) {
67
68
@ Override
68
69
public Map <String , DataType > loadMap () {
69
70
return createDataTypeMap ();
70
71
}
71
72
};
72
-
73
+ conflictMap = new ConflictMap ( mgr . lock );
73
74
}
74
75
75
76
/**
@@ -102,6 +103,7 @@ protected boolean refresh() {
102
103
protected boolean refresh (Record rec ) {
103
104
subcategoryMap .clear ();
104
105
dataTypeMap .clear ();
106
+ conflictMap .clear ();
105
107
106
108
if (isRoot ()) {
107
109
return true ;
@@ -210,13 +212,26 @@ private Map<String, DataType> createDataTypeMap() {
210
212
return map ;
211
213
}
212
214
215
+ private String getBaseName (String dataTypeName ) {
216
+ int indexOf = dataTypeName .indexOf (DataType .CONFLICT_SUFFIX );
217
+ if (indexOf <= 0 ) {
218
+ return dataTypeName ;
219
+ }
220
+ return dataTypeName .substring (0 , indexOf );
221
+ }
222
+
223
+ private boolean isConflictName (String dataTypeName ) {
224
+ return dataTypeName .contains (DataType .CONFLICT_SUFFIX );
225
+ }
226
+
213
227
/**
214
228
* @see ghidra.program.model.data.Category#getCategories()
215
229
*/
216
230
@ Override
217
231
public Category [] getCategories () {
218
232
validate (mgr .lock );
219
- return subcategoryMap .valuesToArray ();
233
+ Collection <CategoryDB > categories = subcategoryMap .values ();
234
+ return categories .toArray (new Category [categories .size ()]);
220
235
}
221
236
222
237
/**
@@ -225,7 +240,8 @@ public Category[] getCategories() {
225
240
@ Override
226
241
public DataType [] getDataTypes () {
227
242
validate (mgr .lock );
228
- return dataTypeMap .valuesToArray ();
243
+ Collection <DataType > dataTypes = dataTypeMap .values ();
244
+ return dataTypes .toArray (new DataType [dataTypes .size ()]);
229
245
}
230
246
231
247
/**
@@ -587,19 +603,144 @@ private void catagoryRenamed(CategoryDB childCategory, String oldName) {
587
603
}
588
604
589
605
void dataTypeRenamed (DataType childDataType , String oldName ) {
590
- dataTypeMap . remove (oldName );
591
- dataTypeMap . put ( childDataType . getName (), childDataType );
606
+ dataTypeRemoved (oldName );
607
+ dataTypeAdded ( childDataType );
592
608
}
593
609
594
- void dataTypeAdded (DataType childDataType ) {
595
- dataTypeMap .put (childDataType .getName (), childDataType );
610
+ void dataTypeAdded (DataType dataType ) {
611
+ String dtName = dataType .getName ();
612
+ dataTypeMap .put (dtName , dataType );
613
+ if (isConflictName (dtName )) {
614
+ conflictMap .addDataType (dataType );
615
+ }
596
616
}
597
617
598
618
void dataTypeRemoved (String dataTypeName ) {
599
619
dataTypeMap .remove (dataTypeName );
620
+ if (isConflictName (dataTypeName )) {
621
+ conflictMap .removeDataTypeName (dataTypeName );
622
+ }
600
623
}
601
624
602
625
void categoryAdded (CategoryDB cat ) {
603
626
subcategoryMap .put (cat .getName (), cat );
604
627
}
628
+
629
+ @ Override
630
+ public List <DataType > getDataTypesByBaseName (String dataTypeName ) {
631
+ List <DataType > list = new ArrayList <>();
632
+ String baseName = getBaseName (dataTypeName );
633
+
634
+ DataType baseType = dataTypeMap .get (baseName );
635
+ if (baseType != null ) {
636
+ list .add (baseType );
637
+ }
638
+
639
+ List <DataType > relatedNameDataTypes = conflictMap .getDataTypesByBaseName (baseName );
640
+ list .addAll (relatedNameDataTypes );
641
+ return list ;
642
+ }
643
+
644
+ /**
645
+ * Class to handle the complexities of having a map as the value in a LazyLoadingCachingMap
646
+ * This map uses the data type's base name as the key (i.e. all .conflict suffixes stripped off.)
647
+ * The value is another map that maps the actual data type's name to the data type. This map
648
+ * effectively provides an efficient way to get all data types in a category that have the
649
+ * same name, but possibly have had their name modified (by appending .conflict) to get around
650
+ * the requirement that names have to be unique in the same category.
651
+ */
652
+ private class ConflictMap extends LazyLoadingCachingMap <String , Map <String , DataType >> {
653
+
654
+ ConflictMap (Lock lock ) {
655
+ super (lock );
656
+ }
657
+
658
+ /**
659
+ * Creates a map of all data types whose name has a .conflict suffix where the key
660
+ * is the base name and {@link LazyLoadingCachingMap} the value is a map of actual name
661
+ * to data type. This mapping is
662
+ * maintained as a lazy cache map. This is only called by the super class when the
663
+ * cached needs to be populated and we are depending on it to acquire the necessary
664
+ * database lock. (See {@link LazyLoadingCachingMap#loadMap()}
665
+ * @return the loaded map
666
+ */
667
+ @ Override
668
+ protected Map <String , Map <String , DataType >> loadMap () {
669
+ Map <String , Map <String , DataType >> map = new HashMap <>();
670
+ Collection <DataType > values = dataTypeMap .values ();
671
+ for (DataType dataType : values ) {
672
+ String dataTypeName = dataType .getName ();
673
+ if (isConflictName (dataTypeName )) {
674
+ String baseName = getBaseName (dataTypeName );
675
+ Map <String , DataType > innerMap =
676
+ map .computeIfAbsent (baseName , b -> new HashMap <>());
677
+ innerMap .put (dataTypeName , dataType );
678
+ }
679
+ }
680
+ return map ;
681
+ }
682
+
683
+ /**
684
+ * Adds the data type to the conflict mapping structure. If the mapping is currently not
685
+ * loaded then this method can safely do nothing. This method is synchronized to provide
686
+ * thread safe access/manipulation of the map.
687
+ * @param dataType the data type to add to the mapping if the mapping is already loaded
688
+ */
689
+ synchronized void addDataType (DataType dataType ) {
690
+ // if the cache is not currently populated, don't need to do anything
691
+ Map <String , Map <String , DataType >> map = getMap ();
692
+ if (map == null ) {
693
+ return ;
694
+ }
695
+
696
+ String dataTypeName = dataType .getName ();
697
+ String baseName = getBaseName (dataTypeName );
698
+ Map <String , DataType > innerMap = map .computeIfAbsent (baseName , b -> new HashMap <>());
699
+ innerMap .put (dataTypeName , dataType );
700
+ }
701
+
702
+ /**
703
+ * Removes the data type with the given name from the conflict mapping structure. If the
704
+ * mapping is currently not loaded then this method can safely do nothing. This method is
705
+ * synchronized to provide thread safe access/manipulate of the map.
706
+ * @param dataTypeName the name of the data type to remove from this mapping
707
+ */
708
+ synchronized void removeDataTypeName (String dataTypeName ) {
709
+ Map <String , Map <String , DataType >> map = getMap ();
710
+ if (map == null ) {
711
+ return ;
712
+ }
713
+ String baseName = getBaseName (dataTypeName );
714
+ Map <String , DataType > innerMap = map .get (baseName );
715
+ if (innerMap == null ) {
716
+ return ;
717
+ }
718
+ innerMap .remove (dataTypeName );
719
+ }
720
+
721
+ /**
722
+ * Returns a list of all data types that have conflict names for the given base name
723
+ * @param baseName the data type base name to search for (i.e. the .conflict suffix removed)
724
+ * @return a list of all conflict named data types that would have the given base name if
725
+ * no conflicts existed
726
+ */
727
+ List <DataType > getDataTypesByBaseName (String baseName ) {
728
+
729
+ // Note that the following call to get MUST NOT be in a synchronized block because
730
+ // it may trigger a loading of the cache which requires a database lock and you
731
+ // can't be synchronized on this class when acquiring a database lock or else a
732
+ // deadlock will occur.
733
+ Map <String , DataType > map = get (baseName );
734
+ if (map == null ) {
735
+ return Collections .emptyList ();
736
+ }
737
+
738
+ // the following must be synchronized so that the implied iterator can complete without
739
+ // another thread changing the map's values.
740
+ synchronized (this ) {
741
+ return new ArrayList <>(map .values ());
742
+ }
743
+ }
744
+
745
+ }
605
746
}
0 commit comments