/* * N M S P C . H * * XML namespace processing * * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved */ #ifndef _EX_NMSPC_H_ #define _EX_NMSPC_H_ #include #include #include #include #include #include // Debugging ----------------------------------------------------------------- // DEFINE_TRACE(Nmspc); #define NmspcTrace DO_TRACE(Nmspc) // Namespaces ---------------------------------------------------------------- // DEC_CONST WCHAR gc_wszXmlns[] = L"xmlns"; // Namespace support functions ----------------------------------------------- // inline ULONG CchGenerateAlias (LONG lAlias, LPWSTR pwszAlias) { UINT i = 0; Assert (pwszAlias); do { // We don't have to use 'A'-'Z', use the first 16 capital // letters to facilitate our computing. // pwszAlias[i++] = static_cast(L'a' + (lAlias & 0xF)); lAlias >>= 4; } while (lAlias); // Ensure termination // pwszAlias[i] = 0; // Return the length // NmspcTrace ("Nmspc: generating '%ws' as alias\n", pwszAlias); return i; } DEC_CONST WCHAR wchHiddenNmspcSep = L'#'; inline BOOL FIsNmspcSeparator (WCHAR wch) { return ((wch == L':') || (wch == L'/') || (wch == L'?') || (wch == L';') || (wch == wchHiddenNmspcSep)); } inline UINT CchAliasFromSizedTag (LPCWSTR pwszTag, UINT cch) { LPCWSTR pwsz; for (pwsz = pwszTag; (pwsz - pwszTag) < static_cast(cch); pwsz++) if (*pwsz == L':') return static_cast(pwsz - pwszTag); return 0; } inline UINT CchNmspcFromTag (UINT cchTag, LPCWSTR pwszTag, LPCWSTR* ppwszOut) { LPCWSTR pwsz; Assert (ppwszOut); *ppwszOut = pwszTag; for (pwsz = pwszTag + cchTag - 1; pwsz >= pwszTag; pwsz--) { if (FIsNmspcSeparator (*pwsz)) { // Since the separator is a part of the namespace, // adjust accourdingly.. // //$ REVIEW: We are being forced down the path of allowing namespaces // that are not properly terminated. The way we do this, is if the // namespace is not properly terminated, or ends in an '#', we will // append an '#' character. // // What this means is the namespace "urn:xml-data", when assembled // into a fully qualified tag would become "urn:xml-data#dt". Also, // the namespace "urn:exch-data#" would become "urn:exch-data##dt". // // What we are catching here is the breaking down of a fully qualified // tag into it's namespace and tag components. // // The length of the namespace will not include the trailing '#' // character -- ever! // *ppwszOut = pwsz + 1; if (wchHiddenNmspcSep == *pwsz) --pwsz; // //$ REVIEW: end. break; } } return static_cast(1 + pwsz - pwszTag); } // class CNmspc -------------------------------------------------------------- // class CNmspc { private: // Ref' counting. // // !!! Please note that this is NON-THREADSAFE !!! // // CXNodes should be operated on a single thread at // any given time. // LONG m_cRef; public: void AddRef() { m_cRef++; } void Release() { if (0 == --m_cRef) delete this; } private: auto_heap_ptr m_pszHref; UINT m_cchAlias; UINT m_cchHref; auto_heap_ptr m_pszLongAlias; WCHAR m_szAlias[20]; LPWSTR m_pszAlias; // Used for the scoping of namespaces // auto_ref_ptr m_pnsScoped; auto_ref_ptr m_pnsSiblings; // non-implemented // CNmspc(const CNmspc& p); CNmspc& operator=(const CNmspc& p); public: CNmspc () : m_cRef(1), // com-style refcounting m_cchHref(0), m_cchAlias(0) { m_szAlias[0] = 0; m_pszAlias = m_szAlias; } SCODE ScSetHref (LPCWSTR pszHref, UINT cch) { Assert (m_pszHref.get() == NULL); // Copy the namespace locally // UINT cb = CbSizeWsz(cch); //$ REVIEW: We are being forced down the path of allowing namespaces // that are not properly terminated. The way we do this, is if the // namespace is not properly terminated, or ends in an '#', we will // append an '#' character. // // What this means is the namespace "urn:xml-data", when assembled // into a fully qualified tag would become "urn:xml-data#dt". Also, // the namespace "urn:exch-data#" would become "urn:exch-data##dt". // // What we catch here, is the handling for an unterminated namespace. // If the namespace ends in a non-terminator or a '#' character, then // we will want to append one. // // It is important to note that the appended char is NOT included in // the total character count of the href. // // If we are dealing with the empty namespace, we will not append a #. // BOOL fUnterminated = FALSE; if (0 != cch) { Assert (pszHref); WCHAR wch = pszHref[cch - 1]; if ((wchHiddenNmspcSep == wch) || !FIsNmspcSeparator(wch)) { NmspcTrace ("Nmspc: WARNING: namespace does not have a separator\n" " as the last character of the namespace. DAV will\n" " add a '#' to the namespace for internal processing.\n"); fUnterminated = TRUE; // Make sure there is space for the appended character // cb += sizeof(WCHAR); } } // //$ REVIEW: end; // Allocate space and copy everything over // m_pszHref = static_cast(ExAlloc(cb)); if (NULL == m_pszHref.get()) return E_OUTOFMEMORY; // Note: CopyMemory does not dereference pszHref if cch equals 0. // CopyMemory (m_pszHref, pszHref, cch * sizeof(WCHAR)); m_cchHref = cch; // If it is unterminated, handle that here // if (fUnterminated) { NmspcTrace ("Nmspc: WARNING: '#' appended to mis-terminated namespace\n"); m_pszHref[cch++] = wchHiddenNmspcSep; Assert (CchHref() == m_cchHref); Assert (PszHref() == m_pszHref.get()); Assert (wchHiddenNmspcSep == m_pszHref[m_cchHref]); } // Ensure termination // m_pszHref[cch] = 0; NmspcTrace ("Nmspc: href defined\n" "-- m_pszHref: %ws\n" "-- m_szAlias: %ws\n", m_pszHref, m_szAlias); return S_OK; } SCODE ScSetAlias (LPCWSTR pszAlias, UINT cchAlias) { // Copy the alias locally // Assert (pszAlias); UINT cb = CbSizeWsz(cchAlias); if (cb <= sizeof(m_szAlias)) { CopyMemory (m_szAlias, pszAlias, cchAlias * sizeof(WCHAR)); m_pszAlias = m_szAlias; } else { m_pszLongAlias.realloc (cb); if (NULL == m_pszLongAlias.get()) return E_OUTOFMEMORY; CopyMemory (m_pszLongAlias, pszAlias, cchAlias * sizeof(WCHAR)); m_pszAlias = m_pszLongAlias.get(); } m_pszAlias[cchAlias] = L'\0'; m_cchAlias = cchAlias; NmspcTrace ("Nmspc: alias defined\n" "-- m_pszHref: %ws\n" "-- m_szAlias: %ws\n", m_pszHref, m_pszAlias); return S_OK; } UINT CchHref() const { return m_cchHref; } UINT CchAlias() const { return m_cchAlias; } LPCWSTR PszHref() const { return m_pszHref; } LPCWSTR PszAlias() const { return m_pszAlias; } // Namespace Scoping ----------------------------------------------------- // CNmspc* PnsScopedNamespace() const { return m_pnsScoped.get(); } void SetScopedNamespace (CNmspc* pns) { m_pnsScoped = pns; } CNmspc* PnsSibling() const { return m_pnsSiblings.get(); } void SetSibling (CNmspc* pns) { m_pnsSiblings = pns; } }; // class CNmspcCache --------------------------------------------------------- // class CNmspcCache { public: typedef CCache > NmspcCache; NmspcCache m_cache; protected: ChainedStringBuffer m_sb; // Key generation // virtual CRCWszN IndexKey (auto_ref_ptr& pns) = 0; // non-implemented // CNmspcCache(const CNmspcCache& p); CNmspcCache& operator=(const CNmspcCache& p); public: CNmspcCache() { INIT_TRACE(Nmspc); } SCODE ScInit() { return m_cache.FInit() ? S_OK : E_OUTOFMEMORY; } void CachePersisted (auto_ref_ptr& pns) { auto_ref_ptr* parp = NULL; CRCWszN key = IndexKey(pns); // Take a quick peek to see if the index already // exists in the cache. If it does, then setup // the scoping such that when the namespace falls // out of scope, the original namespace will be // restored. // if (NULL != (parp = m_cache.Lookup (key))) { NmspcTrace ("Nmspc: scoped redefinition of namespace:\n" "-- old: '%ws' as '%ws'\n" "-- new: '%ws' as '%ws'\n", (*parp)->PszHref(), (*parp)->PszAlias(), pns->PszHref(), pns->PszAlias()); pns->SetScopedNamespace(parp->get()); } // Setup the index // NmspcTrace ("Nmspc: indexing namespace\n" "-- ns: '%ws' as '%ws'\n", pns->PszHref(), pns->PszAlias()); (void) m_cache.FAdd (key, pns); } void RemovePersisted (auto_ref_ptr& pns) { auto_ref_ptr pnsScoped; // Disconnect the index to this namespace // NmspcTrace ("Nmspc: namespace falling out of scope\n" "-- ns: '%ws' as '%ws'\n", pns->PszHref(), pns->PszAlias()); m_cache.Remove (IndexKey(pns)); // If there was an index in existance before this // namespace came into scope, reinstate it here. // pnsScoped = pns->PnsScopedNamespace(); if (pnsScoped.get()) { NmspcTrace ("Nmspc: restoring redefined namespace:\n" "-- restored: '%ws' as '%ws'\n" "-- from: '%ws' as '%ws'\n", pnsScoped->PszHref(), pnsScoped->PszAlias(), pns->PszHref(), pns->PszAlias()); (void) m_cache.FAdd (IndexKey(pnsScoped), pnsScoped); } } }; // class CParseNmspcCache ---------------------------------------------------- // class CParseNmspcCache : public CNmspcCache { // non-implemented // CParseNmspcCache(const CNmspcCache& p); CParseNmspcCache& operator=(const CNmspcCache& p); virtual CRCWszN IndexKey (auto_ref_ptr& pns) { return CRCWszN (pns->PszAlias(), pns->CchAlias()); } // Namespace lookup ------------------------------------------------------ // BOOL FNmspcFromAlias (LPCWSTR pszAlias, UINT cch, auto_ref_ptr& pns) { // In this scenario, the namespace should already exist. // if it doesn't things will not go well. // auto_ref_ptr * parp = NULL; parp = m_cache.Lookup (CRCWszN(pszAlias, cch)); if (parp) { pns = *parp; return TRUE; } return FALSE; } public: CParseNmspcCache() { } // Token translations ---------------------------------------------------- // SCODE TranslateToken (LPCWSTR* ppwszTok, ULONG* pcchTok, LPCWSTR* ppwszNmspc, ULONG* pcchNmspc) { auto_ref_ptr pns; SCODE sc = S_FALSE; Assert (pcchTok && (*pcchTok != 0)); Assert (ppwszTok && *ppwszTok); Assert (pcchNmspc); Assert (ppwszNmspc); // See if there is an namespace that matches the persisted // alias. // if (FNmspcFromAlias (*ppwszTok, *pcchNmspc, pns)) { // Passback the namespace // *pcchNmspc = pns->CchHref(); *ppwszNmspc = pns->PszHref(); //$ REVIEW: We are being forced down the path of allowing namespaces // that are not properly terminated. The way we do this, is if the // namespace is not properly terminated, or ends in an '#', we will // append an '#' character. // // What this means is the namespace "urn:xml-data", when assembled // into a fully qualified tag would become "urn:xml-data#dt". Also, // the namespace "urn:exch-data#" would become "urn:exch-data##dt". // // What we catch here, is the first part of reconstruction of a fully // qualified namespace. If the href we want to pass back is non- // terminated or ends in a '#' character, then we want to append one. // When we cached the namespace object, we did the appending there. // So, the character already exists, the character count just doesn't // include it (see CNmspc::SetHref() above). // // If we are dealing with the empty namespace, we will not append a #. // if (0 != pns->CchHref()) { WCHAR wch = pns->PszHref()[pns->CchHref() - 1]; if ((wchHiddenNmspcSep == wch) || !FIsNmspcSeparator(wch)) { NmspcTrace ("Nmspc: WARNING: namespace is not properly terminated\n" " and DAV will add in a '#' for internal processing.\n"); Assert (wchHiddenNmspcSep == pns->PszHref()[pns->CchHref()]); *pcchNmspc = *pcchNmspc + 1; } } // //$ REVIEW: end. // Adjust the token to refer to the tagname -- ie. the // text after the namespace alias and colon. If the alias // is zero-length, then this maps to the "default" namespace // and no colon skipping is done. // if (0 != pns->CchAlias()) { *pcchTok = *pcchTok - (pns->CchAlias() + 1); *ppwszTok = *ppwszTok + (pns->CchAlias() + 1); } // Tell the caller there is a translation // sc = S_OK; } else { // If the caller expected a namespace, but one did not // exist, it is an error. If they didn't expect one to // to exist -- ie. *pcchNmspc was 0 -- then it is not an // error. Make sure the caller knows what the real // situation is. // if (0 == *pcchNmspc) sc = S_OK; // It looks like no alias was specified, so we can just // return the name as persisted. // *ppwszNmspc = NULL; *pcchNmspc = 0; } return sc; } }; // class CEmitterNmspcCache -------------------------------------------------- // class CEmitterNmspcCache : public CNmspcCache { LONG m_lAlias; // non-implemented // CEmitterNmspcCache(const CEmitterNmspcCache& p); CEmitterNmspcCache& operator=(const CEmitterNmspcCache& p); protected: void AdjustAliasNumber(LONG lOffset) { m_lAlias += lOffset; } // Key generation // virtual CRCWszN IndexKey (auto_ref_ptr& pns) { return CRCWszN (pns->PszHref(), pns->CchHref()); } SCODE ScNmspcFromHref (LPCWSTR pszHref, UINT cch, auto_ref_ptr& pns) { // Lookup to see if the namespace already exists // auto_ref_ptr* parp = m_cache.Lookup (CRCWszN(pszHref, cch)); // If it doesn't exist, then create a new one and cache it // if (parp == NULL) { WCHAR wszAlias[10]; ULONG cchAlias; SCODE sc; // Generate an alias to apply to this namespace and then // check to see if this alias has been already used. If // not, then go ahead an use it. // cchAlias = CchGenerateAlias (m_lAlias++, wszAlias); // Create a new cache item // pns.take_ownership(new CNmspc()); if (NULL == pns.get()) return E_OUTOFMEMORY; // Set the HREF // sc = pns->ScSetHref (pszHref, cch); if (FAILED (sc)) return sc; // Set the alias // sc = pns->ScSetAlias (wszAlias, cchAlias); if (FAILED (sc)) return sc; // It is important, that the key and the return value are taken // from items in the cache, otherwise the lifetime of the data // may not scope the usage. // CachePersisted (pns); return S_FALSE; } pns = *parp; return S_OK; } public: CEmitterNmspcCache() : m_lAlias(0) { } }; #endif // _EX_NMSPC_H_