fileinfo/src/APN.m3


--------------------------------------------------------------------------
MODULE APN;

IMPORT Word, Text, TextSeq, Pathname, MxConfig;
IMPORT TextExtras AS TextEx, TextUtils, SMsg AS Msg;
--------------------------------------------------------------------------
TYPE
  REVEAL T = Default BRANDED "APNType 0.1" OBJECT
    p : Pathname.T;
    hashValue : Word.T := 0;
  METHODS
    hashMe() := HashMe;
    (* empty *)
  OVERRIDES
    init := Init;
    denotation := Denotation;
    isValid := IsValid;
    isAbsolute := IsAbsolute;
  END;
--------------------------------------------------------------------------
PROCEDURE Init(self : T; pn : TEXT; type : Type) : T =
  VAR s : TextSeq.T;
  BEGIN
    IF type = Type.Posix THEN
      s := TextUtils.Split(pn, "/");
      IF Text.GetChar(pn, 0) = '/' THEN
        s.addlo("/");
      ELSE
        s.addlo(NIL);
      END;
      TRY
        self.p := Pathname.Compose(s);
      EXCEPT
        Pathname.Invalid => Msg.Error("invalid pathname: " & pn);
                            self.p := pn;
      END;
    ELSIF type = Type.Win THEN
      s := TextUtils.Split(pn, "\\");
      IF Text.GetChar(pn, 0) = '\\' THEN
        s.addlo("\\");
      ELSIF Text.Length(pn) > 1 AND Text.GetChar(pn, 1) = ':' THEN
        (* FIXME: add support for the more complicated cases of
                  windows file names here *)
        (* skip *)
      ELSE
        s.addlo(NIL);
      END;
      TRY
        self.p := Pathname.Compose(s);
      EXCEPT
        Pathname.Invalid => Msg.Error("invalid pathname: " & pn);
                            self.p := pn;
      END;
    ELSE
      self.p := pn;
    END;
    RETURN self;
  END Init;
--------------------------------------------------------------------------
PROCEDURE HashMe(self : T) =
  BEGIN
    IF NOT caseSensitivePathnames THEN
      self.hashValue := Text.Hash(TextUtils.Lower(self.p));
    ELSE
      self.hashValue := Text.Hash(self.p);
    END;
  END HashMe;
--------------------------------------------------------------------------
PROCEDURE Denotation(self : T; type : Type) : Pathname.T =
  BEGIN
    IF type = Type.Posix OR
       type = Type.Native AND posix THEN
      (* change all backslash characters to slash *)
      IF Text.FindChar(self.p, '\\') > -1 THEN
        self.p := TextUtils.SubstChar(self.p, '\\', '/');
      END;
    ELSIF type = Type.Win OR
          type = Type.Native AND win32 THEN
      (* change all slash characters to backslash *)
      IF Text.FindChar(self.p, '/') > -1 THEN
        self.p :=TextUtils.SubstChar(self.p, '/', '\\');
      END;
    ELSIF NOT posix AND NOT win32 THEN
      Msg.Warning("unknown HOST_OS_TYPE in MxConfig, not converting pathnames");
    END;
    RETURN self.p;
  END Denotation;
--------------------------------------------------------------------------
PROCEDURE IsValid(self : T) : BOOLEAN =
  BEGIN
    RETURN Pathname.Valid(self.p);
  END IsValid;
--------------------------------------------------------------------------
PROCEDURE IsAbsolute(self : T) : BOOLEAN =
  BEGIN
    RETURN Pathname.Absolute(self.p);
  END IsAbsolute;
--------------------------------------------------------------------------
PROCEDURE New(pn : TEXT; type := Type.Default) : T =
  BEGIN
    RETURN NEW(T).init(pn, type);
  END New;
--------------------------------------------------------------------------
PROCEDURE Equal(p, q : T) : BOOLEAN =
  BEGIN
    (* better to compare words than strings *)
    IF p.hashValue = 0 THEN p.hashMe() END;
    IF q.hashValue = 0 THEN q.hashMe() END;
    IF caseSensitivePathnames THEN
      RETURN (p.hashValue = q.hashValue) AND Text.Equal(p.p, q.p);
    ELSE
      RETURN (p.hashValue = q.hashValue) AND TextEx.CIEqual(p.p, q.p);
    END;
  END Equal;
--------------------------------------------------------------------------
PROCEDURE Hash(pn : T) : Word.T =
  BEGIN
    IF pn.hashValue = 0 THEN pn.hashMe() END;
    RETURN pn.hashValue;
  END Hash;
--------------------------------------------------------------------------
PROCEDURE Valid(pn: T): BOOLEAN =
  BEGIN
    RETURN Pathname.Valid(pn.p);
  END Valid;
--------------------------------------------------------------------------
PROCEDURE Absolute(pn: T): BOOLEAN =
  BEGIN
    RETURN Pathname.Absolute(pn.p);
  END Absolute;
--------------------------------------------------------------------------
PROCEDURE Prefix(pn: T): T =
  BEGIN
    RETURN New(Pathname.Prefix(pn.p));
  END Prefix;
--------------------------------------------------------------------------
PROCEDURE Last(pn: T): T =
  BEGIN
    RETURN New(Pathname.Last(pn.p));
  END Last;
--------------------------------------------------------------------------
PROCEDURE Base(pn: T): T =
  BEGIN
    RETURN New(Pathname.Base(pn.p));
  END Base;
--------------------------------------------------------------------------
PROCEDURE Join(pn, base: T; ext: TEXT): T =
  BEGIN
    RETURN New(Pathname.Join(pn.p, base.p, ext));
  END Join;
--------------------------------------------------------------------------
PROCEDURE JoinS(pn:T; base, ext: TEXT): T =
  BEGIN
    RETURN New(Pathname.Join(pn.p, base, ext));
  END JoinS;
--------------------------------------------------------------------------
PROCEDURE LastBase(pn: T): T =
  BEGIN
    RETURN New(Pathname.LastBase(pn.p));
  END LastBase;
--------------------------------------------------------------------------
PROCEDURE LastExt(pn: T): TEXT =
  BEGIN
    RETURN Pathname.LastExt(pn.p);
  END LastExt;
--------------------------------------------------------------------------
PROCEDURE ReplaceExt(pn: T; ext: TEXT): T =
  BEGIN
    RETURN New(Pathname.ReplaceExt(pn.p, ext));
  END ReplaceExt;
--------------------------------------------------------------------------
VAR
  posix := Text.Equal(MxConfig.HOST_OS_TYPE, "POSIX");
  win32 := Text.Equal(MxConfig.HOST_OS_TYPE, "WIN32");
BEGIN
  caseSensitivePathnames := NOT Text.Equal(MxConfig.HOST_OS_TYPE, "WIN32");
END APN.

interface TextUtils is in: