@@ -329,46 +329,34 @@ object IcebergReflection extends Logging {
329329 * Different Iceberg versions expose file paths differently:
330330 * - Newer versions: location() returns String
331331 * - Older versions: path() returns CharSequence
332+ *
333+ * `None` means neither accessor is declared; a genuine invoke failure propagates instead.
332334 */
333- def extractFileLocation (contentFileClass : Class [_], file : Any ): Option [String ] = {
334- try {
335- findMethod(contentFileClass, " location" ) match {
336- case Some (locationMethod) => Some (locationMethod.invoke(file).asInstanceOf [String ])
337- case None =>
338- findMethod(contentFileClass, " path" )
339- .map(_.invoke(file).asInstanceOf [CharSequence ].toString)
340- }
341- } catch {
342- case _ : Exception => None
335+ def extractFileLocation (contentFileClass : Class [_], file : Any ): Option [String ] =
336+ findMethod(contentFileClass, " location" ) match {
337+ case Some (locationMethod) => Some (locationMethod.invoke(file).asInstanceOf [String ])
338+ case None =>
339+ findMethod(contentFileClass, " path" )
340+ .map(_.invoke(file).asInstanceOf [CharSequence ].toString)
343341 }
344- }
345342
346343 /**
347344 * Extracts file location from ContentFile instance using dynamic class lookup.
348345 */
349- def extractFileLocation (file : Any ): Option [String ] = {
350- try {
351- val contentFileClass = loadClass(ClassNames .CONTENT_FILE )
352- extractFileLocation(contentFileClass, file)
353- } catch {
354- case _ : Exception => None
355- }
356- }
346+ def extractFileLocation (file : Any ): Option [String ] =
347+ tryLoadClass(ClassNames .CONTENT_FILE ).flatMap(extractFileLocation(_, file))
357348
358349 /**
359350 * The file format of a ContentFile (data or delete file), e.g. "PARQUET", "AVRO", "ORC".
360351 *
361352 * `contentFileClass` is the public ContentFile interface, which callers already hold: Iceberg's
362353 * concrete file impls are package-private, so `format()` resolved on the concrete class throws
363354 * IllegalAccessException when invoked.
355+ *
356+ * `None` means `format()` isn't declared; a genuine invoke failure propagates instead.
364357 */
365- def getFileFormat (contentFileClass : Class [_], file : Any ): Option [String ] = {
366- try {
367- findMethod(contentFileClass, " format" ).map(_.invoke(file).toString)
368- } catch {
369- case _ : Exception => None
370- }
371- }
358+ def getFileFormat (contentFileClass : Class [_], file : Any ): Option [String ] =
359+ findMethod(contentFileClass, " format" ).map(_.invoke(file).toString)
372360
373361 /**
374362 * Gets the Iceberg Table from a SparkScan.
@@ -786,20 +774,18 @@ object IcebergReflection extends Logging {
786774 * An Iceberg DeleteFile object
787775 * @return
788776 * List of field IDs used in equality deletes, or empty list for position deletes
777+ *
778+ * Empty means either `equalityFieldIds()` isn't declared, or it returned `null` (Iceberg's
779+ * normal contract for a position-delete file). A genuine invoke failure propagates instead of
780+ * collapsing into empty.
789781 */
790- def getEqualityFieldIds (deleteFileClass : Class [_], deleteFile : Any ): java.util.List [_] = {
791- try {
792- val ids =
793- getMethod(deleteFileClass, " equalityFieldIds" )
794- .invoke(deleteFile)
795- .asInstanceOf [java.util.List [_]]
796- if (ids == null ) new java.util.ArrayList [Any ]() else ids
797- } catch {
798- case _ : Exception =>
799- // Position delete files return null/empty for equalityFieldIds
800- new java.util.ArrayList [Any ]()
782+ def getEqualityFieldIds (deleteFileClass : Class [_], deleteFile : Any ): java.util.List [_] =
783+ findMethod(deleteFileClass, " equalityFieldIds" ) match {
784+ case None => new java.util.ArrayList [Any ]()
785+ case Some (method) =>
786+ val ids = method.invoke(deleteFile).asInstanceOf [java.util.List [_]]
787+ if (ids == null ) new java.util.ArrayList [Any ]() else ids
801788 }
802- }
803789
804790 /**
805791 * Gets field name and type from schema by field ID.
0 commit comments