Custom parse exceptions

Feb 18, 2012 at 7:14 AM

hi Sam,

Wanted to put a suggestion on the table; 

I always develop with 'Break on Exception' turned ON in Visual Studio. It gives me a good feel for bad things that are creeping up on me. However, there are a a few parsing exceptions that pop up so frequently in FlickrNet that this becomes impractical.

One of them is relatively easy to silence:

 

       public static DateTime ParseDateWithGranularity(string date)
        {
            // LWM:
            DateTime output = DateTime.MinValue;

            string format = "yyyy-MM-dd HH:mm:ss";
            // LWM:
            if( date.Length>0 ) // avoid zillion exceptions
            try
            {
                output = DateTime.ParseExact(date, format, System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None);
            }
            catch (FormatException)
            {
                if (Regex.IsMatch(date, @"^\d{4}-00-01 00:00:00$"))
                {
                    output = new DateTime(int.Parse(date.Substring(0, 4), System.Globalization.NumberFormatInfo.InvariantInfo), 1, 1);
                }
                else
                {
                    output = DateTime.MinValue;
                }
            }
            return output;
        }

 

Another case needs a bit more work: when you call PhotosGetExif() on a photo that doesn't share its Exif properties.

There are a lot of those, I can tell you :-)

I looked for a way to fix this; my approach was to throw a custom exception in this particular case (e.g. FlickrExifNotPublicException) and then tell Visual Studio not to break on this particular exception.

I couldn't really find a good place to put this extra logic: there is no base-class where I could add a "virtual CreateCustomException( int code, string message  )", which I could then override in ExifTagCollection or ExifTag and call from ExceptionHandler.CreateResponseException()

So, I ended up with a lame approach: to pass an optional CustomExceptionFactory argument all the way from PhotosGetExif(), via GetResponseCache<T>( .. ) via GetResponse<T>( .. ) and finally to ExceptionHandler.CreateResponseException()

So:

 

        public ExifTagCollection PhotosGetExif(string photoId, string secret)
        {
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("method", "flickr.photos.getExif");
            parameters.Add("photo_id", photoId);
            if (secret != null) parameters.Add("secret", secret);

            return GetResponseCache<ExifTagCollection>(parameters, new ExifExceptionFactory() /*LWM*/ );
        }

// AND:

        private T GetResponseCache<T>(Dictionary<string, string> parameters, CustomExceptionFactory factory = null /*LWM*/ ) where T : IFlickrParsable, new()
        {
            return GetResponse<T>(parameters, Cache.CacheTimeout, factory /*LWM*/ );
        }

// AND:

        private T GetResponse<T>(Dictionary<string, string> parameters, TimeSpan cacheTimeout, CustomExceptionFactory factory = null /*LWM*/ ) where T : IFlickrParsable, new()
        {

            // ...........

            while (reader.MoveToNextAttribute())
            {
                if (reader.LocalName == "stat" && reader.Value == "fail")
                    throw ExceptionHandler.CreateResponseException(reader, factory /*LWM*/ );
                continue;
            }

            // ...........

            return item;

        }


// AND (finally)

        public static Exception CreateResponseException(XmlReader reader, CustomExceptionFactory factory = null /*LWM*/)
        {
 
           // ...........

            /*LWM*/
            if (factory != null)
                return factory.CreateException(code, msg);
            else
                return CustomExceptionFactory.CreateFlickrApiException(code, msg);
        }
   

 

Supported by these trivial classes:

 

   public class CustomExceptionFactory
    {
        public virtual Exception CreateException(int inCode, string inMessage)
        {
            return CreateFlickrApiException(inCode, inMessage);
        }
        public static Exception CreateFlickrApiException(int inCode, string inMessage)
        {
            return new FlickrApiException(inCode, inMessage);
        }
    }

// AND:
 

   // LWM:
    public class FlickrExifNotPublicException : FlickrApiException
    {
        public FlickrExifNotPublicException(int inCode, string inMessage) : base(inCode, inMessage) { }
    }

// AND:

    public class ExifExceptionFactory : CustomExceptionFactory
    {
        public override Exception CreateException(int inCode, string inMessage)
        {
            return new FlickrExifNotPublicException(inCode, inMessage);
        }
    }

 

This works, but feels conceptually wrong. It's just too much hassle to pass on this factoryclass all the way through the system. It would be better if the template argument for GetResponseCache<T>( ) could be used to create a custom exception, or maybe another method could be added to IFlickrParsable that handles cases like this (or creates a custom exception).  

Finally, perhaps the Load( ) method in ExifTag or ExifTagCollection could be tweaked to handle the "non-public exif" case without throwing an exception at all, by just creating a default ExiftTagCollection with one element that says "[not shared]" as this code does, after the call to PhotosGetExif( ) 

 

                    try
                    {
                        mResultExifTags = inLogin.mFlickr.PhotosGetExif(mPhotoID);
                    }
                    catch (FlickrApiException inFlickrApiException)
                    {
                        switch (inFlickrApiException.Code)
                        {
                            case 1: // photo not found
                                inInvalidPhotoIDs.Add(mPhotoID);
                                return false;
                            case 2: // exif is not public
                                mResultExifTags = new ExifTagCollection();
                                mResultExifTags.PhotoId = mPhotoID ;
                                ExifTag tag = new ExifTag();
                                tag.Label = "Exif data" ;
                                tag.Raw = "[not shared]";
                                mResultExifTags.Add(tag);
                                return true;
                        }
                        throw;
                    }
     

 

 

 

 

 

 


 

 

 

 

 

 

Coordinator
Feb 18, 2012 at 12:14 PM

The first one looks like a bug - could you give me example calls so I can reproduce when empty string is getting passed to the parse date function?

The second one sounds like a lot of work just to get a special kind of exception thrown. I could change it so that a different exception is throw for each error code that Flickr sends back, but the single digit ones tend to be the same code for each function, but a different error.

Feb 19, 2012 at 1:33 PM

re: example 
calling PeopleGetInfo( "18387778@N00" ) this results in date == ""

calling PeopleGetInfo( "47963952@N03" ) this results in date == "0000-00-00 00:00:00"


re: special kind of exception
You mean like: 
if( code == 2 ) throw new FlickrApiException2( code, msg ) ;
if( code == 3 ) throw new FlickrApiException3( code, msg ) ;
It's not the most elegant solution, and it's a bit hard to predict up-front if I'd want to ignore all FlickrApiException2's being thrown, but yes it gives me some options to ignore some off them without turning 'break on exception' off alltogether. 

Coordinator
Feb 21, 2012 at 1:04 PM

Right, I've checked in the fix for the two date issues you mention, so they don't use try/catch any more.

I've started adding custom exceptions, but there are well over a hundred different exceptions, so could take some time to add them all.

Sam

Coordinator
Mar 15, 2012 at 5:00 PM

Custom exceptions (for at least some of the exceptions) are now in place.

Mar 15, 2012 at 5:53 PM
Sounds good, thanks. Will give it a try!


From: [email removed]
To: [email removed]
Date: Thu, 15 Mar 2012 09:01:04 -0700
Subject: Re: Custom parse exceptions [FlickrNet:324665]

From: samjudson
Custom exceptions (for at least some of the exceptions) are now in place.
Read the full discussion online.
To add a post to this discussion, reply to this email (FlickrNet@discussions.codeplex.com)
To start a new discussion for this project, email FlickrNet@discussions.codeplex.com
You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on CodePlex.com.
Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com