1 /**
2  * \file Author.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10 
11 #include <config.h>
12 
13 #include "Author.h"
14 
15 #include "support/convert.h"
16 #include "support/gettext.h"
17 #include "support/lassert.h"
18 #include "support/lstrings.h"
19 
20 #include <algorithm>
21 #include <istream>
22 
23 using namespace std;
24 using namespace lyx::support;
25 
26 namespace lyx {
27 
computeHash(docstring const & name,docstring const & email)28 static int computeHash(docstring const & name,
29 	docstring const & email)
30 {
31 	string const full_author_string = to_utf8(name + email);
32 	// Bernstein's hash function
33 	unsigned int hash = 5381;
34 	for (unsigned int i = 0; i < full_author_string.length(); ++i)
35 		hash = ((hash << 5) + hash) + (unsigned int)(full_author_string[i]);
36 	return int(hash);
37 }
38 
39 
Author(docstring const & name,docstring const & email)40 Author::Author(docstring const & name, docstring const & email)
41 	: name_(name), email_(email), used_(true),
42 	  buffer_id_(computeHash(name, email))
43 {}
44 
45 
Author(int buffer_id)46 Author::Author(int buffer_id)
47 	: name_(convert<docstring>(buffer_id)), email_(docstring()), used_(false),
48 	  buffer_id_(buffer_id)
49 {}
50 
51 
nameAndEmail() const52 docstring Author::nameAndEmail() const
53 {
54 	if (email().empty())
55 		return name();
56 	else
57 		return bformat(_("%1$s[[name]] (%2$s[[email]])"), name(), email());
58 }
59 
60 
valid() const61 bool Author::valid() const
62 {
63 	//this cannot be equal if the buffer_id was produced by the hash function.
64 	return name_ != convert<docstring>(buffer_id_);
65 }
66 
67 
operator ==(Author const & l,Author const & r)68 bool operator==(Author const & l, Author const & r)
69 {
70 	return l.name() == r.name() && l.email() == r.email();
71 }
72 
73 
operator <<(ostream & os,Author const & a)74 ostream & operator<<(ostream & os, Author const & a)
75 {
76 	// FIXME UNICODE
77 	os << a.buffer_id_ << " \"" << to_utf8(a.name_) << "\"";
78 	if (!a.email_.empty())
79 		os << " " << to_utf8(a.email_);
80 
81 	return os;
82 }
83 
84 
operator >>(istream & is,Author & a)85 istream & operator>>(istream & is, Author & a)
86 {
87 	string s;
88 	is >> a.buffer_id_;
89 	getline(is, s);
90 	// FIXME UNICODE
91 	a.name_ = from_utf8(trim(token(s, '\"', 1)));
92 	a.email_ = from_utf8(trim(token(s, '\"', 2)));
93 	return is;
94 }
95 
96 
author_smaller(Author const & lhs,Author const & rhs)97 bool author_smaller(Author const & lhs, Author const & rhs)
98 {
99 	return lhs.bufferId() < rhs.bufferId();
100 }
101 
102 
AuthorList()103 AuthorList::AuthorList()
104 {}
105 
106 
record(Author const & a)107 int AuthorList::record(Author const & a)
108 {
109 	bool const valid = a.valid();
110 	// If we record an author which equals the current
111 	// author, we copy the buffer_id, so that it will
112 	// keep the same id in the file.
113 	if (valid && !authors_.empty() && a == authors_[0])
114 		authors_[0].setBufferId(a.bufferId());
115 
116 	Authors::const_iterator it = authors_.begin();
117 	Authors::const_iterator const beg = it;
118 	Authors::const_iterator const end = authors_.end();
119 	for (; it != end; ++it) {
120 		if (valid && *it == a)
121 			return it - beg;
122 		if (it->bufferId() == a.bufferId()) {
123 			int id = it - beg;
124 			if (!it->valid())
125 				// we need to handle the case of a valid author being registred
126 				// after an invalid one. For instance, because "buffer-reload"
127 				// does not clear the buffer's author list.
128 				record(id, a);
129 			return id;
130 		}
131 	}
132 	authors_.push_back(a);
133 	return authors_.size() - 1;
134 }
135 
136 
record(int id,Author const & a)137 void AuthorList::record(int id, Author const & a)
138 {
139 	LBUFERR(unsigned(id) < authors_.size());
140 	authors_[id] = a;
141 }
142 
143 
recordCurrentAuthor(Author const & a)144 void AuthorList::recordCurrentAuthor(Author const & a)
145 {
146 	// current author has id 0
147 	record(0, a);
148 }
149 
150 
get(int id) const151 Author const & AuthorList::get(int id) const
152 {
153 	LASSERT(id < (int)authors_.size() , return authors_[0]);
154 	return authors_[id];
155 }
156 
157 
begin() const158 AuthorList::Authors::const_iterator AuthorList::begin() const
159 {
160 	return authors_.begin();
161 }
162 
163 
end() const164 AuthorList::Authors::const_iterator AuthorList::end() const
165 {
166 	return authors_.end();
167 }
168 
169 
sort()170 void AuthorList::sort()
171 {
172 	std::sort(authors_.begin(), authors_.end(), author_smaller);
173 }
174 
175 
operator <<(ostream & os,AuthorList const & a)176 ostream & operator<<(ostream & os, AuthorList const & a)
177 {
178 	// Copy the authorlist, because we don't want to sort the original
179 	AuthorList sorted = a;
180 	sorted.sort();
181 
182 	AuthorList::Authors::const_iterator a_it = sorted.begin();
183 	AuthorList::Authors::const_iterator const a_end = sorted.end();
184 
185 	for (; a_it != a_end; ++a_it) {
186 		if (a_it->used() && a_it->valid())
187 			os << "\\author " << *a_it << "\n";
188 	}
189 	return os;
190 }
191 
192 
193 } // namespace lyx
194