- Latest version: The latest
info.odma.practical100 components are at <http://ODMA.info/dev/devNotes/2006/10/d061001b.htm>.
- Previous version: 0.30alpha <http://ODMA.info/dev/devNotes/2006/10/d061001f.htm>.
- Next version: 0.59beta <http://ODMA.info/dev/devNotes/2006/10/d061001h.htm>
is the transition candidate that will provide a half-step toward
0.60beta with all breaking changes identified.
- This version: 0.56beta Evolutionary
Development 0.11 <http://ODMA.info/dev/devNotes/2006/10/d061001g.htm>
consists of material that is intended for the
practical100 release candidate. Future changes were to
be limited to documentation formatting changes (e.g., for JavaDoc)
and repair of defects. There may also be some tightening
of parameter-format filtering before 0.60beta. [This
objective is amended in the 0.59beta work.]
- Downloads: The material of the 0.56beta
Evolutionary Development is provided in four files:
-
d061001g-license.txt
0.00 version, 3,215 byte file dated 2007-02-23-12:16
-
d061001g-ReleaseNotes.txt
0.07, 14,562 byte file dated 2007-03-10-23:30
-
d061001g.txt
0.04 Manifest of the 0.56beta package, 10,335 byte
file dated 2007-03-10-23:43
-
d061001g-practical100-0.56beta.zip
0.04 Archive of the 0.56beta Material, 37,623 byte file dated 2007-03-10-23:44.
The license, release notes, and manifest are included.
Review the manifest to determine whether it is desirable to
download the complete archive.
WARNING: Versions 0.00-0.03 of the
0.56beta archive are obsolete. They must be replaced by
an 0.04 or later archive to work properly with the
0.57beta release of the odmjni100 package. Changes in
0.04 correct a serious defect in OdmFormat.wfDocIdPrefix.
In addition, there are minor adjustments to the manifest and ReleaseNotes.
1. Objectives
The ODMJNI 1.0 0.56beta distribution is provided to accomplish six
important steps in the progression to full ODMJNI 1.0:
- Begin clean-up and improved documentation of the
practical100
package in anticipation of interface freeze and initiation of the
initial-public 0.60beta release.
- Repair any defects noticed during usage of the
practical100 package as it has existed since the 0.30alpha
iteration.
- Introduce the info.odma.practical100.OdmFormat class and static
methods for verifying the format of text data elements. This
allows verification of parameters to practical100 interface and
class implementations without submission to, and summary rejection
by, an implementation.
- Enable active filtering of parameters in the null
implementations within the practical100 package. The encounter of
any ill-formed text element will result in an unchecked
OdmError
exception as specified in the definition of
practical100 behaviors.
- Increase the test coverage of null implementations by providing
more coverage of all paths that are possible under null conditions.
(The testing of all of the filter and exception cases isn't complete
until 0.60beta. In particular, the 0.56beta changes do not
make any modifications to the odmjni100 package, which must only
undergo a regression check.)
- Perform regression against the 0.54beta odmjni100 implementations
to ensure that the introduction of validations does not interfere
with the valid format usages with existing
odmjni100 classes and tests.
Because the odmjni100 package classes use
practical100 null
implementations for base classes, some parameter filtering will
occur via inheritance.
2. Bugs and Caveats
Versions of the OdmFormat Document ID methods had a serious defect in
packages prior to 0.04. The bug was such that
OdmFormat.wfDocIdPrefix
and all other methods that relied on it (prefixDmsId,
wfDocId, and safeDocId)
would fail for all Document ID strings, well-formed or not. This bug
is repaired in package version 0.04.
3. Change Summary
This version of the info.odma.practical100
package introduces the OdmFormat class and its use within the
practical100
package.
All changes are backward compatible with the final
0.30beta version of practical100. This package should work in all
previous settings where the 0.30beta components are usable. 0.56beta
is a cumulative roll-up of all practical100 changes to date. It may be
used with any ODMJNI 1.0 release
from 0.25alpha to 0.56beta and beyond.
There are some implementation changes and new classes and methods. The
contracts for existing classes and methods are honored fully.
These are the change highlights:
- The new method OdmNullBind.application(appId,
appWindow) is added.
This method corresponds to the OdmJniBind.application(appId,
appWindow)
method, so that the null implementation can be substituted directly in
the testing of an application.
- The OdmFormat class is implemented, along with methods for verifying
that text strings are well-formed as ODMA-important parameters and
results. The methods are
- boolean wfAppId(java.lang.String appId)
well-formed, pure alphanumeric ODMA Application ID of 1 to 15 characters
- boolean wfDocFormatName(java.lang.String
docFormatName)
well-formed, '.'-prefixed pure alphanumeric ODMA Document Format ID
- boolean wfDocIdPrefix(java.lang.String docId)
well-formed case-insensitive "::ODMA\\odmdmsid\\" prefix on an
arbitrary-string; odmdmsid is from 1 to 8 alphanumeric characters
that are to be taken as the ODMA DMS ID.
- java.lang.String prefixDmsId(java.lang.String
docId)
the ODMA DMS ID (i.e., the non-null odmdmsid part) when
wfDocIdPrefix holds,
null otherwise
- boolean wfDocId(java.lang.String docId)
true
when the string starts with a well-formed Document ID prefix, does not
exceed the maximum length for Document IDs, and does not contain any
Unicode C0 or C1 controls or the codes for non-breaking space or soft
hyphen. This solution fails to reject Unicode characters that
cannot be translated to the single-byte code page of the ODMA DMS.
See the Developer Notes for further details on how this approach was
arrived at (section 4).
- boolean safeDocId(java.lang.String docId)
for a well-formed ODMA Document Id that is further restricted to consist
of the ISO 646 alphanumeric characters and a few additional characters that can be used in interchange practically
anywhere; the only non-alphanumeric characters are:
: - _ . / \
- The formats of parameters to methods of the null implementations are
enforced and OdmError exceptions are thrown when a parameter string
value is not well-formed. In previous implementations, operations
would fail silently. The OdmError exception is part of the
defined behavior and compatible with the documented contract for
behavior of the practical100 interfaces and classes.
4. Development Approach
The development consists of the addition of validation operations and
confirmation that the validations are being used properly (exceptions are
thrown) and that the validations do not disturb any of the code that is
known to be using well-formed data. More rigorous coverage tests will
come later.
OdmFormat is first implemented with all-null behaviors: the filters
always return false regardless of the operand. Verification using the
filters is added at all of the appropriate places in the
practical100 null
classes. This will automatically confirm that
OdmError is being
thrown and what that looks like.
After this case, and after each subsequent change, we run a version of
the practical100\test\Null01 which we will be improving as we go.
After each confirmation with Null01 and its improvements, we also run a
regression on odmjni100. The test programs for
odmjni100 may or may
not reveal any sensitivity to these changes. Whatever sensitivity
there is should come from the use of class inheritance from
practical100
null classes. In general, there should not be any need to make
odmjni100 repairs, but there may be a list of changes that are warranted
when odmjni100 is updated for 0.58beta.
The filters are given non-null implementations in the following sequence:
- OdmFormat.wfAppId
allowing OdmNullBind(appId [, appWindow]) to succeed via the success of
OdmNullConnection construction
- OdmFormat.wfDocFormatName
allowing OdmConnection.acceptNewDocument(docFormat) to succeed at
returning a null implementation
- OdmFormat.wfDocId
allowing OdmConnection.openKnownDocument(docId) to succeed at returning
a null failure implementation
That concludes the basic development and confirmation. There is no
internal practical100 usage of the
wfDocIdPrefix,
prefixDmsId, and safeDocId
methods.
5. Developer Notes: Working toward
0.56beta
- 2007-02-24: Step 1 - Define the 10-step work
breakdown to operate against for this drop.
- 2007-02-25: Step 2 - Make an
OdmNullFormat class that
always fails. Make an OdmFormat class that extends
OdmNullFormat
and initially does not overload any OdmNullFormat methods.
In creating these files, I split out OdmNullFormat simply to illustrate
the null behavior base-class technique that is also used in other parts
of ODMJNI. This particular case involves inheritance static
implementations, and I am using this to confirm how Java works in this
case.
When setting up the initial null behaviors, it occurred to me that these
filters might contradict the implementations at the Native100 level
(which are required for defensive-programming and modularity reasons).
It would be an improvement for ODMJNI classes and Native100 to share a
set of common filters and I left a comment marker to look into that
later on. This is an odd refactoring/optimization case that tends
to increase coupling, so it is worth exploring to see how to confine the
dependency problem that accompanies having consistent (i.e., shared)
behavior between the ODMJNI and OdmNative layers.
- 2007-02-27: There was considerable effort to confirm the
extent to which ODMA is Multi-Document Interface (MDI) unfriendly.
Through ODMA 2.0 (introduced before Office 2000), applications are
expected to provide a single Window Handle (HWND) that will persist as
long as the single ODMA Connection. This single window is used as
the parent for all DMS-initiated modal dialogs. This creates
some problems for ODMA-aware applications that present unique windows
for separate documents, and the first window opened need not be the last
one closed. Time was spent confirming that Microsoft Office Word
2003 and Microsoft Office Word 2007 are also unable to overcome this
limitation with ODMA. The two versions of Word also differ
somewhat in the behaviors that their MDI implementations exhibit when
ODMA operations are involved. The most surprising aspect of ODMA's
failure to accommodate MDI-style applications is that there has never
been a complaint about it since ODMA 2.0 was released in 1998.
- 2007-02-28: Modify the BuildClasses.bat file to always erase
the *.class files before any build. This works around a problem
where the Java compiler will fail to recompile a failed class
compilation if the class that depends on it compiled and appears to be
current.
- 2007-02-28: Step 3 - Review the
practical100
interface classes to make sure that there are accurate throws clauses
for all methods. None needed to be changed.
- 2007-02-28: Step 4 - Add format checks in the
practical100 null implementations. This includes addition of
OdmNullBind.application(appId, appWindow) to
OdmNullBind.java.
The OdmNullConnection class is kept
public as a requirement of having
the interface be public. The constructor is made
protected so that
there is no longer any support for direct construction of an
OdmNullConnection outside of the package except by extension, as is done
with the OdmJniApp class . A second constructor is also added:
OdmNullConnection(appId, appWindow).
- 2007-02-28: Step 5 - This is enough for a clean compile and initial testing, since the
appId
check will fail at once. This is confirmed with
Null01, CheckSwing
(OdmClicker), and
Check04 (CheckNew and
CheckChoice). The static
inheritance of OdmFormat from
OdmNullFormat is also sorted out.
This is not a full Steps 4-5. Instead of adding all format
checks, I only updated OdmNullConnection, since we will be blocked at the
appId parameter until the correct
wfAppId is put in place. So I will
add more checks as the possibility of more failures comes into existence.
- 2007-03-01: Step 6 - Implement OdmFormat.wfAppId.
The rules are borrowed from verifyAppId in
OdmNative100.cpp 0.28 (0.52beta).
The private constant OdmFormat.MAX_APPID_LENGTH is also added. The
value is derived from the current Odma32types100.h header file as one less
than ODM_APPID_MAX (which includes a trailing
'\0' code that does not apply
to the Java String value).
With this change, RunNull01 0.00 now throws an
OdmError because of
ill-formed Document Format Name to acceptNewDocument.
At this point, I noticed that it is time to make OdmError messages
consistent and to use interfaceImplementation() in the message where that is
available. So I went back to document how that works in
OdmInterface
and OdmError. All
practical100 classes were reviewed for the format of
interfaceImplementation() and of the
OdmError strings. These
changes complete Steps 4-5. There's no place to add more checks in the
practical100 package.
Now, all that's left is the regression to make sure nothing in the
odmjni100 layer has been broken that
shouldn't be (because of extended null classes that now have deficient
format checks). All of the odmjni100
CheckSwing and
Check04 confirmation checks now operate without problems.
The practical100
Null01 exercise fails on a document format error, which is
appropriate.
- 2007-03-01: Step 7 - Implement
OdmFormat.wfDocFormatName. The private constant
OdmFormat.MAX_DOCFORMAT_LENGTH is introduced and defined to be one less than
ODM_FORMAT_MAX as defined in
Odma32types100.h. The string must begin
with a period and consist of one or more additional characters in the same
character set as appId.
There may be reason to relax this format restriction in the future,
depending on rules for what can appear in registry entries, what file
systems allow, and so on.
The practical100
Null01 exercise operates without any failure. There
are going to be no more odmjni100 regression cases, since nothing failed
when wfDocFormatName was always
false. The test cases will have to
be tightened up to show anything more exciting. That will be done as
part of 0.58beta or even 0.60beta.
- 2007-03-01: Step 8 - Implement OdmFormat.wfDocId.
There's nothing to test this with until I come up with a better set of null
tests. I think it might be better to make some improvements in the
null-behavior tests, although I'm not entirely clear that it worthwhile for
0.56beta. I might want to leap directly to 0.58 beta. My
inclination is that I need some testing of DocId formats in a standalone
test of some kind. I will look at that as part of Step 9 cleanup work.
The current wfDocId is basically a length and
wfDocIdPrefix check.
There should be some filtering of character codes beyond the prefix, but it
is not necessary that there be any. This is something to look into as
well.
- 2007-03-02: Rethinking wfDocId. [I crashed and lost
all of my notes for today. This is a reconstruction.]
There needs to be some sort of screening on wfDocId that somehow enhances
interchange of Document IDs. I am going to follow Dick Wilson's
architectural principle: It is easier to relax a restriction later
rather than add one later. So I will start by making a
safeDocId
filter that indicates when a DocId is pretty much always safe for use in
interchange, including being of a reasonable length for e-mail, etc.
The wfDocId will be a little more relaxed, but I will start out making sure
that it is confined to the Unicode Basic Latin code page simply because
those are pretty much the codes where we do not care what the
single-/multi-byte code page is.
The ODMA 2.0-3 specification only gives us the discussion in
Section 1.1, Document IDs with an additional caveat in section
1.7 Character Sets. The caveat about interconvertibility of
Document IDs does not take into consideration that prospect that there is no
equivalent character between two code pages. So we settle on what
works for almost all code-page settings.
With regard to what Windows tolerates, the Basic Class Library Team has a
blog page on character set limitations here: Kim Hamilton's
first of three. The MSDN
page on the
rules, allows all characters but control codes and allows code-page
sensitivity (all octet values are allowed except for 0 to 31). There
are a few reserved characters (< > : " / \ |). Also, the names in
long-file-name format are stored in Unicode. This is not very helpful
for us, although we at least understand how it is that
MAX_PATH is 260 in
Windows, with 248 the maximum for a directory name.
Although Document IDs need not have anything to do with file names, it is
commonplace that they have some similarity and that they even may have a
fairly-direct mapping to filenames as used within the DMS.
- 2007-03-03: More Reconsideration. I am not thrilled
about the safeDocId filter and wonder if it should be dropped completely.
Also, the fact that wfDocId filters Unicode characters is the reason for the
tight restriction to ASCII characters. We do not know what the
single-byte code page will be for communication to the ODMA DMS, so we can't
be sure of the successful communication of Document IDs with any further
characters. Any relaxation to wfDocId requires operation at the native
level and the safeDocId filter would still restrict to at most the Basic
Latin graphical character codes.
I read Oren Eine's article on
The Production Value of Seams today. His illustration of testable
design had me wonder whether I should have an interface above
OdmFormat to
allow for a variety of testable-design scenarios. I gave up on that
when I remembered that Java does not allow interfaces on classes, only on
instantiated objects. I am not so willing to go that abstract above
what is merely a suite of static methods. I may regret it, but that is
all I am doing for now. There is time to reconsider up until
0.60beta and probably beyond that.
The real challenge to deal with has to do with shared native-Windows filter
code that can work between both ODMJNI and Native100. Maybe that will
show something that influences the choice to interface in a decisive way.
- 2007-03-03: Step 9 - Wrap-Up. Once I got into
0.56beta, it was clear that the changes are confined to the
practical100
package and there is no regression to odmjni100 and Native100. The
real test comes with 0.58beta and modifications to
odmjni100. Although
I am concerned that the filters have not been tested very well, I think I
will address that starting in 0.58beta. It is time to simply wrap up
0.56 beta, providing the necessary files and manifests.
- The d061001g-license.txt file is reviewed to make sure that it remains
applicable (it does).
- The d061001g-ReleaseNotes.txt is reviewed and updated
- The d061001g.txt manifest is created
- The d061001g-practical100-0.56beta.zip archive of the material is
created.
- 2007-03-04: Simplification. As part of Step 9, I
also chose against a full refresh of the practical100 development tree.
This is a small, incremental change, and it is more useful to package just
the changes. The next full refresh will be the 0.60beta.
- 2007-03-04: Step 10. This page is updated and all of
the material is uploaded. The d061201h
0.60beta progression page is updated to reflect availability of
0.56beta.
- 2007-03-06: Archive Version 0.01 Adjustment to
wfDocId. In order to
support integration of the odmjni100 0.57beta package, the
OdmFormat.wfDocId
method was changed again. Since OdmFormat.wfDocId was first given a
non-null implementation, there have been three versions of the function.
It is an interesting progression.
The first version provided the following code:
if (docId.length() > MAX_DOCID_LENGTH) return false;
return wfDocIdPrefix(docId);
Basically, there must be a valid prefix, but any Unicode UTF-16 codes are
permitted after the prefix so long as the length restriction is satisfied.
This is a simple way to do it, but it is far too relaxed about what can come
after the prefix. To improve on this, I went to the following approach
in the 0.00 release of the 0.56beta archive:
if (docId.length() > MAX_DOCID_LENGTH) return false;
if (!wfDocIdPrefix(docId)) return false;
for (int i = ODMPREFIX.length(); i < docId.length(); i++)
{ char code = docId.charAt(i);
if (code < ' ' || code > '~') return false;
}
return true;
This is the version that was settled on for archive version 0.00.
The idea is to only allow the printable graphic codes of the Unicode Basic
Latin character set. These are essentially the printable ASCII or ISO
646 codes.|
In all cases, the constant ODMPREFIX is the string whose value is the seven
characters “::ODMA\” that was
already verified in wfDocIdPrefix, along with the following DMS ID and
another “\”.
Now, this version although very safe (but not so safe as
safeDocId), it more
restrictive than the ODMA 2.0 specification requires of DMS integrations.
The one risk of this particular filter, is that it will reject ODMA Document
IDs that were legitimately produced by a DMS.
I have placed restrictions in
wfAppId and wfDocFormatName that are
stricter than required by ODMA. But these are restrictions imposed on
new Java applications, not on any existing applications, and I can argue
that the added safety and utility of the restrained forms is part of the
practical100 contract.
In considering the newly-introduced 0.57beta release, I concluded that the
most effective solution is to use the translatability of the
docId string to
the single-byte codes of the default Windows ANSI code page to be the
ultimate requirement for characters following the prefix, including the DMS
ID. I can't implement that in Java alone, I need to use JNI code
for that. In the 0.56beta, I can't be that tight, but I can relax to a
point where all such strings will pass through, even though strings that
don't translate will also be passed, for now. When 0.58beta
substitutes JNI implementations of filters used by both
practical100 and the
rest of the ODMJNI 1.0 implementation, the thrown exceptions will reject
exactly the strings that will lead to silent failures in
odmjni100 JNI code.
This is the first form I entertained for the 0.01 archive update was the
original version considered for 0.00, above. The problem with that is
that it still permits far more than is ever intended to be acceptable.
It is also so permissive that known control codes are passed and must be
filtered out by supplemental code in 0.57beta. So I finally settled on
this version:
if (docId.length() > MAX_DOCID_LENGTH) return false;
if (!wfDocIdPrefix(docId)) return false;
for (int i = 0; i < docId.length(); i++)
{ char code = docId.charAt(i);
if (code < 0x20
|| (code > 0x7e && code < 0xa1)
|| code == 0xad
)
return false;
/* XXX: Although the non-break space (0xa0) and
the soft hyphen (0xad) are technically not
control characters, their allowance in
Document IDs would be pernicious.
*/
}
return true;
This version rules out the C0 and C1 Control Codes (and two more) of the
first 256 Unicode UTF-16 values. We can't reject more without knowing
the code page, but translatability to the proper code page, without
exceeding the length limitation, is all that remains for a precise check.
The idiosyncratic ODMPREFIX usage is also
avoided, since those already strictly-checked codes will simply be accepted
a second time.
- 2007-03-07: Re-issue the archive. The 0.01 archive
did not include the latest .class
files. While repairing that, the OdmFormat.java version was included
correctly, the ReleaseNotes.txt warning was updated, and the manifest was
also updated with correction for missing linebreaks. This new package
is version 0.02.
Somehow, the revised approach to wfDocId seems much more straightforward and
practical. When (in 0.58beta) it also rejects Document ID strings that
are not translatable to the default code page for communication to a DMS, I
think it will be entirely acceptable.
Later, small discrepancies were noticed in the Release Notes and the
manifest, leading to issuing of an 0.03 package and an updated version of
this page. We are at the borderline of corrections to the housekeeping
being hard to make without introducing errors in the housekeeping.
More automation would be good.
- 2007-03-09: Accumulation of Small Defects. Since the
posting of package 0.03, I've been noticing small defects in the manifest
and Release Notes pages. I have been marking printed copies for
changes to be made the next time there is some reason to make an update.
So long as the code is fine, I will accumulate changes in the turnover
documents. They will be worked in as bugs are repaired or the
material is made obsolete. The same applies to earlier packages.
- 2007-03-10: Here's a Bug! When I created the
CheckKnown.java test for 0.57beta (the first-ever code that tests
OdmFormat
Document ID checking), I discovered that all candidate Document IDs were
being rejected by the OdmJniApp.openKnownDocument implementation. The
bug is in this statement of OdmFormat.wfDocIdPrefix(docId):
if ( docId.substring(0, ODMPREFIX.length()-1)
.compareToIgnoreCase(ODMPREFIX)
!= 0)
return false;
When I planted some additional output statements into the CheckKnown test
code, it became clear that
docId.substring(0, "::ODMA\\".length()-1)
== "::ODMA"
and not "::ODMA\\" as expected. A
little digging in the Java API documentation explains what is wrong with my
use of the java.lang.String.substring(start, end). I
made a classic beginner mistake in not noticing that end is the
position after the end, not the end itself. That is,
docId.substring(0, 0) returns an empty string and
docId.substring(0,1) is
the string which has docId.charAt(0) as its only character.
The repaired OdmFormat will be included in the 0.04 package and all
previous 0.56beta package versions will be declared obsolete.
- Attribution:
- Hamilton, Dennis E.
- info.odma.practical100 Component Development -- 0.56beta
Evolutionary Development. AIIM ODMA Interoperability Exchange,
ODMdev Development Note d061001 page d061001g 0.11, September 23, 2007. Available at <http://ODMA.info/dev/devNotes/2006/10/d061001g.htm>.
- Revision History:
- 0.11 2007-09-23-17:00 Reflect Start of 0.59beta
work
- The 0.59beta placeholder is linked as the next version. That page
does not have the latest materials at first, but it can be accessed to
observe the progression of activity that leads to the availability of
candidate materials.
- 0.10 2007-08-19-15:45 Repaving Complete
- All editorial cleanups and repaving-required modifications are
introduced.
- 0.09 2007-03-10-18:04 Create package 0.04 with
OdmFormat bug fix
- This opportunity is used to correct some minor defects in the
transmittal and wrapper material.
- 0.08 2007-03-07-16:52 Make Simple Corrections to
the Package
- It is desirable to use 0.03, which makes small corrections to the
manifest and Release Notes. The software is not changed from 0.02.
- 0.07 2007-03-07-11:43 Clean up the Revised
0.56beta Packaging
- The 0.02 archive version, with related updates, is produced to correct
the identification of the OdmFormat.java file and to include class files
that are compiled with that version of the file. The source code and
the class files were out of synch in the package. The descriptions of
wfDocId and safeDocId on this page also had to be corrected.
- 0.06 2007-03-06-18:17 Revise 0.56beta to support
0.57beta
- The OdmFormat.wfDocId method is altered to fit with the direction being
taken for 0.57beta as an interim step toward 0.58beta where the final
OdmFormat methods will be established using native implementations.
- 0.05 2007-03-04-18:52 Complete Step 10 and make
0.56beta available
- 0.04 2007-03-03-16:48 Start Steps 9 and 10.
- 0.03 2007-03-01-21:21 Document through Step 8.
- 0.02 2007-02-24-21:06 Add Development Approach
- 0.01 2007-02-23-11:14 Update with Draft Release
Notes
- The draft notes are linked to the page and additional structure and
narrative is provided as accompaniment to the release notes.
- 0.00 2007-02-21-19:38 Provide Placeholder for
Commencement of 0.56beta Development
- This page is put up as a placeholder so that the 0.56beta version of
practical100 development can be captured.
|
|
created 2007-02-21-19:38 -0800 (pst) by
orcmid
$$Author: Orcmid $
$$Date: 08-04-23 21:11 $
$$Revision: 105 $
|