Doc-Based, Need to open a text-based document

I am upgrading an older AppleScript Studio document-based application that used to save documents as text files with it’s own extension. I need my new AS Obj-C app to be able to read the old documents. Can I do that?

Right now I am getting this error:

[<NSDocument 0x7fff74b64450> valueForUndefinedKey:]: this class is not key value coding-compliant for the key NSError. (error -10000)

Perhaps I need to inform my application that it should be able to read it’s documents like text instead of key-value??

Can anyone help?

I don’t think that error is related. Throw in lots of log statements to find where the error is being thrown.

The error occurs on this (default) code:

if outError is not missing value then
	set contents of outError to my NSError's errorWithDomain_code_userInfo_(my NSOSStatusErrorDomain, my unimpErr, missing value)
end if

It says:

readFromData:ofType:error:]: [<NSDocument 0x7fff74b64450> valueForUndefinedKey:]: this class is not key value coding-compliant for the key NSError. (error -10000)

I’m not actually doing anything with the data coming in, just logging it and it’s class. It is:

NSConcreteData

What the heck is that and how do I turn it into text. The Xcode help guide gives no results for this.

Does this reveal anything?

on readFromData_ofType_error_(data, typeName, outError)
	log outError
	-- Result = DocumentType

Yes – it means you’re trying to use the template-provided handlers, which are broken. See chapter 22 of my book for details.

The main problem with it, though, is that it’s attempting to use the reserved AS word “data” as a parameter variable. Change it to something like theData.

Okay, that got me a LOT closer!

You don’t happened to talk about NSConcreteData in your book do you? Or, know offhand how to convert that into “regular” text data?

My data is coming in like this:

<646c6532 00000000 75747874 00000530 000d0050 00720069 006e0074 00200044 00610074 0065003a 004d006f 006e0064 00610079 000d0041 00700070 006c0069 00630061 00740069 006f006e 003a0051 00750061 0072006b 00580050 00720065 00730073 00200037 002e0030 000d0054 0065006d 0070006c 00610074 0065003a 00530074 0061006e 00640061 00720064 00200054 0065006d 0070006c 00610074 0065000d 004d006f 00640069 00660069 00630061 00740069 006f006e 00200049 006e0066 006f003a 004c0061 00730074 0020006d 006f0064 00690066 00690065 00640020 00620079 0020004d 00610072 006b0020 004d0075 006e0072 006f002c 00200046 00720069 00640061 0079002c 0020004d 00610079 00200031 0038002c 00200032 00300031 00320020 0035003a 00300038 003a0033 00380020 0050004d 000d0056 00650072 00730069 006f006e 003a0031 002e0035 002e0030 00300031 000d0050 00610074 0068003a 00570054 004d0020 00530065 00720076 00650072 003a0057 006f0072 006b003a 0043006c 00690065 006e0074 003a0053 006f006c 00750074 0069006f 006e0073 003a0052 0065006c 00690061 006e0063 00650020 00560069 00740061 006d0069 006e003a 0043006f 006d0070 0061006e 00790020 00520065 0073006f 00750072 00630065 0073003a 00540065 00730074 0020004d 00610074 00650072 00690061 006c003a 00530065 00720076 00650072 00200053 0069006d 0075006c 00610074 0069006f 006e003a 00520065 006c0069 0061006e 00630065 00200053 00650072 00760065 0072003a 004e0065 00780050 00720065 00730073 00200028 00540065 00730074 0029003a 000d0057 0069006e 0064006f 00770020 0042006f 0075006e 00640073 003a0031 00380033 00200036 00350030 00200038 00380037 00200039 00370039 00200025 000d0053 00740061 00720074 00200043 006f006d 00700061 006e0079 003a004e 00650077 00200052 006f0077 000d0053 00740061 00720074 0020004c 00610062 0065006c 003a000d 00470072 006f0075 0070003a 0041002d 0046000d 0043006c 00690065 006e0074 003a000d 00520045 004c0020 00530074 006f0063 006b0020 00490074 0065006d 003a0059 00650073 000d0050 0072006f 00640075 00630074 00200046 0061006d 0069006c 0079003a 000d0050 0072006f 00640075 00630074 00200053 0069007a 0065003a 000d0042 00610063 006b0067 0072006f 0075006e 0064003a 00440065 00660061 0075006c 0074000d 00430065 006e0074 00650072 00200050 0061006e 0065006c 003a0044 00650066 00610075 006c0074 000d0055 00500043 003a0044 00650066 00610075 006c0074 000d0054 00650078 00740020 0043006f 006c006f 0072003a 00440065 00660061 0075006c 00740020 00280046 0072006f 006d0020 004d0061 00730074 00650072 00200046 0069006c 00650029 000d0054 00650078 00740020 0043006f 006c006f 00720020 00410064 00640072 00650073 0073003a 00440065 00660061 0075006c 00740020 00280046 0072006f 006d0020 004d0061 00730074 00650072 00200046 0069006c 00650029 000d0054 00650078 00740020 0043006f 006c006f 00720020 00500072 006f0064 00750063 00740020 004e0061 006d0065 003a0044 00650066 00610075 006c0074 00200028 00460072 006f006d 0020004d 00610073 00740065 00720020 00460069 006c0065 0029000d 00520065 00700065 00610074 003a0031 000d0053 00740061 00720074 0020006e 00650077 00200070 00610067 0065003a 004e006f 000d004e 006f0074 00650073 003a000d 0025000d>

NSConcreteData is just a subclass of NSData. Assuming it represents text, you use something like:

set theString to current application's NSString's alloc()'s initWithData_encoding_(theData, current application's NSUTF8StringEncoding)

That worked, sort of. It returned the first four characters of the first paragraph of the text in the document. but there are 1344 characters in 26 paragraphs total.

???

What’s the file contain?

Actually, looking at your data, it might well be UTF16. Whatever you saved it as from the old doc, you need to use the matching encoding enum to read it again.

Changing 8 to 16 in that line makes the four characters come in as chinese so I don’t’ think that’s correct.

set theString to current application's NSString's alloc()'s initWithData_encoding_(theData, current application's NSUTF16StringEncoding)

This was just text saved by an AS Studio app… so text saved by AppleScript using the write to file command with no special stuff at all. A plain text file. However, it has a custom extension to make it’s icon show up.

When I open it with text editor, it just shows up as several paragraphs of text.

dle2utxt0
Print Date:Monday
Application:QuarkXPress 7.0
Template:Standard Template
Modification Info:Last modified by Mark Munro, Friday, May 18, 2012 5:08:38 PM
Version:1.5.001
Path:WTM Server:Work:Client:Solutions:Reliance Vitamin:Company Resources:Test Material:Server Simulation:More Stuff:
Window Bounds:183 650 887 979 %
Start Company:New Row
Start Label:
Group:A-F
Client:
REL Stock Item:Yes
Product Family:
Product Size:
Background:Default
Center Panel:Default
UPC:Default
Text Color:Default (From Master File)
Text Color Address:Default (From Master File)
Text Color Product Name:Default (From Master File)
Repeat:1
Start new page:No
Notes:
%

It’s definitely Unicode of some flavour – notice all the nulls (00) in the data. You probably had Unicode text when you saved – there was at least one version of the OS where the write command would save that as Unicode. It even says “utxt” in the first line. If you wrote it out a bit at a time, it might be partly Unicode and partly MacRoman. That looks the case, given the first few characters look like ASCII/MacRoman.

Try the other UTF encodings.

Yes, I remember that OS… it might have been the one the old version of this app was built in.

One note, I am saving the document on 10.7 with the old app and trying to open it with the new app and it’s failing. I imagine the text encoding is burned into the old app as it was from the OS it was built on?

In any event, it looks like I’ll need to use a new extension and have my client use an upgrade file menu item or something that will simply read the file as a text file and then convert it to the new document format or something. Less than ideal but I’m not seeing another alternative.

For the record, I tried all of these encodings:

NSUTF8StringEncoding
NSASCIIStringEncoding
NSUnicodeStringEncoding
NSMacOSRomanStringEncoding
NSUTF32StringEncoding
NSProprietaryStringEncoding

All produced the same error:

Can’t make «class ocid» id «data kptr00000000E8C6D175FF7F0000» into type string. (error -1700)

I also tried this from those old days:

try
	set textDocument to (textDocument as Unicode text) as string
	set textDocument to «class ktxt» of (textDocument as record)
end try
-- result = Can't make «class ocid» id «data kptr00000000E8C6D175FF7F0000» into type string. (error -1700)

Any other thoughts, however obscure would be appreciated.

Okay, this is frustrating… Now I simply want to put in a dialog to stop and warn the user to use an upgrade menu instead of opening the document. However, I can’t detect the different class.

if class of textDocument = "NSConcreteData" then

doesn’t work because class of textDocument isn’t a string

if class of textDocument = NSConcreteData then

doesn’t work because the variable NSConcreteData isn’t’ defined.

ARGH!

Email me the document.

If you want to check the class of a variable, use isKindOfClass_. That also works with subclasses, so you can use isKindOfClass_(current application’s NSData). But I don’t see how that’s going to help you; readFromData:ofType:error: is always going to return data.

There’s nothing odd about the file. It doesn’t seem to use any non-ASCII characters and it reads fine here with either of these:

set x to current application's NSString's stringWithContentsOfFile_encoding_error_("/Users/shane/Desktop/TEST OLD.RELV", current application's NSUTF8StringEncoding, missing value)
set x to current application's NSString's stringWithContentsOfFile_encoding_error_("/Users/shane/Desktop/TEST OLD.RELV", current application's NSMacOSRomanStringEncoding, missing value)

If I read it as data first, I get what you’re getting passed:

10:01:48.377 [6] set y to current application's NSData's dataWithContentsOfFile_("/Users/shane/Desktop/TEST OLD.RELV")
--> <<showing first 128 of 1364 bytes>>
<646c6532 00000000 75747874 00000544 000d0050 00720069 006e0074 00200044 00610074 0065003a 004d006f 006e0064 00610079 000d0041 00700070 006c0069 00630061 00740069 006f006e 003a0051 00750061 0072006b 00580050 00720065 00730073 00200037 002e0030 000d0054 0065006d 0070006c 00610074 0065003a>
10:01:48.378 [7] set z to current application's NSString's alloc()'s initWithData_encoding_(y, current application's NSUTF8StringEncoding)
--> dle2utxtD
Print Date:Monday
Application:QuarkXPress 7.0
Template:Standard Template
Modification Info:Last modified by Mark Munro, Tuesday, May 22, 2012 7:26:02 PM
Version:1.5.001
Path:WTM Client:Solutions:Reliance Vitamin:Company Resources:Test Material:Server Simulation:Reliance Server:NexPress (Test):
Window Bounds:47 1071 751 1400 %
Start Company:Test - 182755
Start Label:-
Group:A-Z
Client:Test - 182755
REL Stock Item:Yes
Product Family:
Product Size:
Background:Default
Center Panel:Default
UPC:Default
Text Color:Default (From Master File)
Text Color Address:Default (From Master File)
Text Color Product Name:Default (From Master File)
Repeat:1
Start new page:No
Notes:
%

So maybe you’re looking in the wrong place for the problem…

Well, I don’t have control over how it’s loaded because the data is handed to me by the readFromData_ofType_error_

I’ll play around with isKindOfClass_ and stopping the user from doing this and get them to run an conversion process instead. Then I can load the data myself and convert it.

No go.

on readFromData_ofType_error_(textDocument, typeName, outError)
	set blnClassMatch to textDocument's isKindOfClass_(current application's NSData)
	log "blnClassMatch=" & blnClassMatch

This returns true, tell me the data is a match. Perhaps because the document being opened has the correct extension?

Is there a way to ask the incoming data what class it is and get a response that I can compare to something… like:

if class of textDocument = NSConcreteData then DO THIS

?

Or, I guess I could just switch extensions for the new app. That should work.

You’re missing the point: as long as you use readFromData_ofType_error_, you’re always going to get data. You’re essentially asking for the file as a collection of bytes, which you then have to convert to whatever you think they should be. Changing extensions isn’t going to change anything in that regard.

You need to take this passed in data, and convert it to text using something like:

set theString to current application's NSString's alloc()'s initWithData_encoding_(theData, current application's NSUTF8StringEncoding)

That does work.

You also have to do the reverse when saving.

If that’s all too hard, you can get rid of readFromData_ofType_error_ and dataOfType_error_, and replace them with readFromURL_ofType_error_ and writeToURL_ofType_error_. In that case you will be passed a URL for the file, and you can read it directly yourself.